RecyclerView使用GridLayoutManager 设置间距一致大小

在android应用中,要实现一个Recycleview,使用GridLayoutManager格子排列,且排列成4列
实现水平方向间距均等(没有外边距)。

(均分为3列5列等、竖直方向、有边距等原理相同。)

先看最终效果图。

---
xml中这样配置

<androidx.recyclerview.widget.RecyclerView
    android:background="#bbffbb"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:spanCount="4"
    tools:listitem="@layout/layout_item"/>

为了醒目,我们让RecyclerView背景是浅绿色。

每一个item的配置如layout_item.xml所示

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:background="#ffcccc"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:gravity="center"
        android:textSize="12sp"
        app:layout_constraintEnd_toEndOf="@+id/thumb"
        app:layout_constraintStart_toStartOf="@+id/thumb"
        app:layout_constraintTop_toBottomOf="@+id/thumb"
        tools:text='NAME' />

    <com.google.android.material.imageview.ShapeableImageView
        android:id="@+id/thumb"
        android:layout_width="65dp"
        android:layout_height="65dp"
        android:scaleType="fitXY"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:background="@drawable/item_bg" />

</androidx.constraintlayout.widget.ConstraintLayout>

为了醒目,我们让每一个item的背景色为浅红色。

Activity中,我们这样设置

class MyItemDecorator:RecyclerView.ItemDecoration(){
    val spanCount = 4
    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        val position = parent.getChildAdapterPosition(view)
        val column =position% spanCount //第几列
    }
}

        binding.recycleHot.addItemDecoration(MyItemDecorator())

这样的显示效果,就是,最左边item的左边距离0,最右边item的右边距离不是0。

如果layout_item.xml里root布局

 android:layout_width="match_parent"

显示效果为:

 ---

在函数

getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    )


    view.width始终是0,不管item的root布局layout_widht是match_parent还是wrap_content。
    

---
终极解法 ,

 

我们设每个item的左右边距分别是L0、R0、L1、R1、L2、R2、L3、R3。


 

我们要求边距相同,即 

R0+L1 =  R1+L2 = R2+L3 = space(间距)    

每一个item占的地方是一样的,即

L0+item内宽度+R0 = item外宽度

L1+item内宽度+R1 = item外宽度

L2+item内宽度+R2 = item外宽度

L3+item内宽度+R3 = item外宽度

L0+R0 = L1+R1 = L2+R2 = L3+R3 = item外宽度-item内宽度          (设为p)

我们知道L0==R3==0
故可以推算出

L0 = 0
R0 = p-L0= p
L1 = space - R0 = space-p
R1 = p-L1 = p-(space-p) =  p*2 -space
L2 = space -R1 = sapce - (p*2-space) = space*2 - p*2(根据对称==R1==p*2-space)
R2 = p-L2 = p-(space*2-p*2) = p*3 - space*2(根据对称==L1==space-p)
L3 = space-R2 = space-(p*3-space*2) = space*3 -p*3(根据对称==R0==p)
R3 = 0

据此,

改善代码   

 class MyItemDecorator : RecyclerView.ItemDecoration() {
        val spanCount = 4
        override fun getItemOffsets(
            outRect: Rect,
            view: View,
            parent: RecyclerView,
            state: RecyclerView.State
        ) {
            val position = parent.getChildAdapterPosition(view)
            val column = position % spanCount
            fun dp2px(dp: Int): Int {
                val scale = view.resources.displayMetrics.density
                return (dp * scale + 0.5f).toInt()
            }
            val itemWidth = parent.width / 4  //item外宽度
            val itemWidthInside = dp2px(65) //item内宽度
            val padding = itemWidth - itemWidthInside // p
            val space = (parent.width - 4 * itemWidthInside) / 3 // space
            if (column == 0) {
                outRect.left = 0
                outRect.right = padding
            } else if (column == 1) {
                outRect.left = space - (padding)
                outRect.right = padding * 2 - space
            } else if (column == 2) {
                outRect.left = space * 2 - padding * 2
                outRect.right = padding * 3 - space * 2
            } else if (column == 3) {
                outRect.left = padding
                outRect.right = 0
            }

        }
    }

layout_item.xml也要改,把root组件的layout_width固定。

android:layout_width="65dp"

这样就能实现GridLayoutManager是4列排布,各个item间距相同,最左边和最右边的item距离边框距离为0的效果。

 

如果RecycleView里设置了padding,比如

```
            <androidx.recyclerview.widget.RecyclerView
                android:background="#bbffbb"
+               android:paddingHorizontal="16dp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
                app:spanCount="4"
                tools:listitem="@layout/layout_item"/>

此时,应该在Activity中,要把parent.Width改为parent.Width - parent.paddingLeft -parent.paddingRight

   override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        val position = parent.getChildAdapterPosition(view)
        val column = position % spanCount
        fun dp2px(dp: Int): Int {
            val scale = view.resources.displayMetrics.density
            return (dp * scale + 0.5f).toInt()
        }

        val parentWidth = parent.width - parent.paddingLeft - parent.paddingRight
        val itemWidth = parentWidth / 4
        val itemWidthInside = dp2px(65)
        val padding = itemWidth - itemWidthInside
        val space = (parentWidth - 4 * itemWidthInside) / 3
        when (column) {
            0 -> {
                outRect.left = 0
                outRect.right = padding
            }
            1 -> {
                outRect.left = space - (padding)
                outRect.right = padding * 2 - space
            }
            2 -> {
                outRect.left = space * 2 - padding * 2
                outRect.right = padding * 3 - space * 2
            }
            3 -> {
                outRect.left = padding
                outRect.right = 0
            }
        }
    }

效果为

 

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>