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