在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
- }
- }
- }
效果为