開發(fā)筆記-GridLayoutManager實現(xiàn)Item均勻分布

背景描述

最近開發(fā)中遇到需求悴品,需要實現(xiàn)圖片的格子分布效果,如下圖所示


格子圖片

其中要求:

  1. 圖片與圖片的間隔简烘,圖片與屏幕的左邊距苔严,以及圖片與屏幕的右邊距,都為固定大小孤澎,比如10dp届氢。
  2. 圖片寬高相等。


代碼實現(xiàn)

主要代碼如下

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val images = listOf(...)
        val imageAdapter = ImageAdapter(images)
        rv_images.apply {
            layoutManager = GridLayoutManager(context, 4, RecyclerView.VERTICAL, false)
            addItemDecoration(EvenItemDecoration(dp2px(context, 10), 4))
            adapter = imageAdapter
        }
    }

    class ImageAdapter(private val images: List<Int> = listOf()) : RecyclerView.Adapter<ImageAdapter.ViewHolder>() {
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            val view = LayoutInflater.from(parent.context).inflate(R.layout.item_image, parent, false)
            // 動態(tài)設(shè)置圖片大小 保證寬高相等
            val ivSize = (getScreenContentWidth(parent.context) - dp2px(parent.context, 10) * 5) / 4
            view.iv_image.layoutParams.height = ivSize
            return ViewHolder(view)
        }

        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            holder.view.iv_image.setImageResource(images[position])
        }
        ...
    }

    class EvenItemDecoration(private val space: Int, private val column: Int) : RecyclerView.ItemDecoration() {
        override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
            val position = parent.getChildAdapterPosition(view)
            // 每個span分配的間隔大小
            val spanSpace = space * (column + 1) / column
            // 列索引
            val colIndex = position % column
            // 列左覆旭、右間隙
            outRect.left = space * (colIndex + 1) - spanSpace * colIndex
            outRect.right = spanSpace * (colIndex + 1) - space * (colIndex + 1)
            // 行間距
            if (position >= column) {
                outRect.top = space
            }
        }
    }
}

布局item_image.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<ImageView
    android:id="@+id/iv_image"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="fitXY"
    tools:src="@mipmap/ic_launcher"
    android:background="#ffccff"/>

其中需要注意是

  1. 圖片的寬度在布局和代碼中不能寫死退子,需要自適應(yīng)。
  2. 在代碼(Adapter)中根據(jù)屏幕寬度來計算圖片高度型将。
  3. 在ItemDecoration中計算間隔寂祥。


分析-GridLayoutManger分配Item空間的機制

GridLayoutManger在繪制子View的時候,會先為它們分配固定的空間七兜。
比如丸凭,我們這里是4列,則會為每個圖片分配父布局寬度(這里是屏幕寬度) / 4的寬度大小的空間腕铸。

  1. 每個圖片會占據(jù)自己的空間惜犀,且不會超出這個空間。如果超出了恬惯,比如設(shè)置了很大的固定寬度向拆,則超出的部分會被遮蓋。
  2. 如果使用ItemDecoration設(shè)置間隔酪耳,則間隔會占用當前圖片的空間,圖片會被壓縮。

示例一-設(shè)置固定大寬度
在Adapter代碼中設(shè)置圖片的寬度并在去掉ItemDecoration

// ImageAdapter
...
val ivSize = (getScreenContentWidth(parent.context) - dp2px(parent.context, 10) * 5) / 4
view.iv_image.layoutParams.width = 1000
view.iv_image.layoutParams.height = ivSize
...

// MainActivity 中注釋掉EvenItemDecoration
...
// addItemDecoration(EvenItemDecoration(dp2px(context, 10), 4))
...

結(jié)果如下:

寬度設(shè)置為1000px的圖片

通過Studio自帶Layout Inspector可以查看每個圖片的左右位置碗暗,第一行圖片的mLeft分為0颈将、360、720言疗、1080晴圾,對應(yīng)的mRightmLeft + 1000。
以上示例說明

  1. 每個圖片占據(jù)的父布局的寬度是一樣的噪奄,為父布局寬度除以列數(shù)死姚,這里是1440 / 4 = 360。
  2. 每個圖片超出所占據(jù)空間寬度后的部分會被后面的圖片或屏幕遮蓋勤篮。

示例二-設(shè)置較大的左右間隔
修改EvenItemDecoration代碼如下:

    class EvenItemDecoration(private val space: Int, private val column: Int) : RecyclerView.ItemDecoration() {
        override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
            outRect.left = space
            outRect.right = space
        }
    }

運行結(jié)果如下:

有間隔的Grid

通過Layout Inspector查看每個圖片的左右位置都毒,第一行圖片的mLeft分為35(0 +35)、395(360+35)碰缔、755(720+35)账劲、1115(1080+35),對應(yīng)的mRight分別是325(360-35)金抡、685(720-35)瀑焦、1045(1080-35)、1405(1440-35)梗肝。
說明設(shè)置的間隔的確占用了對應(yīng)圖片之前分配的空間榛瓮。

最后,根據(jù)以上機制巫击,不難理解EvenItemDecoration中的代碼榆芦。

Github源代碼地址:https://github.com/zhangliangnbu/gridlayout-demo

提示:文章首發(fā)我的個人博客簡書

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市喘鸟,隨后出現(xiàn)的幾起案子匆绣,更是在濱河造成了極大的恐慌,老刑警劉巖什黑,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件崎淳,死亡現(xiàn)場離奇詭異,居然都是意外死亡愕把,警方通過查閱死者的電腦和手機拣凹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恨豁,“玉大人嚣镜,你說我怎么就攤上這事¢倜郏” “怎么了菊匿?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵付呕,是天一觀的道長。 經(jīng)常有香客問我跌捆,道長徽职,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任佩厚,我火速辦了婚禮姆钉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘抄瓦。我一直安慰自己潮瓶,他們只是感情好,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布钙姊。 她就那樣靜靜地躺著毯辅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪摸恍。 梳的紋絲不亂的頭發(fā)上悉罕,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音立镶,去河邊找鬼壁袄。 笑死,一個胖子當著我的面吹牛媚媒,可吹牛的內(nèi)容都是我干的嗜逻。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼缭召,長吁一口氣:“原來是場噩夢啊……” “哼栈顷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嵌巷,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤萄凤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后搪哪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體靡努,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年晓折,在試婚紗的時候發(fā)現(xiàn)自己被綠了惑朦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡漓概,死狀恐怖漾月,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情胃珍,我是刑警寧澤梁肿,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布蜓陌,位于F島的核電站,受9級特大地震影響栈雳,放射性物質(zhì)發(fā)生泄漏护奈。R本人自食惡果不足惜缔莲,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一哥纫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧痴奏,春花似錦蛀骇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至檐晕,卻和暖如春暑诸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辟灰。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工个榕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芥喇。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓西采,卻偏偏與公主長得像,于是被迫代替她去往敵國和親继控。 傳聞我的和親對象是個殘疾皇子械馆,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351