RecyclerView GridLayoutManager 等

2023-07-13  本文已影响0人  萧关逢候骑11

https://juejin.cn/post/6844904116859174926

学习一下
RecyclerView GridLayoutManager 等分间距

还是卤蛋

2020年04月07日 12:06 ·  阅读 17117

RecyclerView 表格实现

RecyclerView 配合GridLayoutManager 可以实现类似表格的样式,为了实现均分,adapter 的布局宽度改为匹配父元素,即 android:layout_width="match_parent" 。

ini

复制代码

RecyclerViewrvPhotoAlbums= findViewById(R.id.rv_photoAlbums_content);rvPhotoAlbums.setLayoutManager(new GridLayoutManager(this, 4));rvPhotoAlbums.setAdapter(new PhotoAlbumsAdapter());

效果图如下:

添加间距 ItemDecoration

ItemDecoration 源码分析

androidx.recyclerview.widget.RecyclerView#addItemDecoration(androidx.recyclerview.widget.RecyclerView.ItemDecoration)。 先简单看下 ItemDecoration 里的方法:

less

复制代码

publicabstractstaticclassItemDecoration{// ....省略其他方法介绍...../**    * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies    * the number of pixels that the item view should be inset by, similar to padding or margin.    * The default implementation sets the bounds of outRect to 0 and returns.    * 解读:获取给定item view的偏移量,用outRect 表示。outRect的每个字段分别表示不同方向的偏移量,类似于填充或边距。    * 默认设置为0,即没有间距。    *    * <p>    * If this ItemDecoration does not affect the positioning of item views, it should set    * all four fields of <code>outRect</code> (left, top, right, bottom) to zero    * before returning.    * 如果不想影响item view 的位置,需要置outRect 的left, top, right, bottom 为0    *    * <p>    * If you need to access Adapter for additional data, you can call    * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the    * View.    *    * @param outRect Rect to receive the output.    * @param view    The child view to decorate    * @param parent  RecyclerView this ItemDecoration is decorating    * @param state  The current state of RecyclerView.    */publicvoidgetItemOffsets(@NonNullRect outRect,@NonNullView view,@NonNullRecyclerView parent,@NonNullState state) {getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),                parent);    }}

通过代码注释我们知道,可以通过继承 androidx.recyclerview.widget.RecyclerView.ItemDecoration 类处理间距。

自定义ItemDecoration

less

复制代码

publicclassGridSpaceItemDecorationextendsRecyclerView.ItemDecoration{privateintmSpanCount;//横条目数量privateintmRowSpacing;//行间距privateintmColumnSpacing;// 列间距/**    * @param spanCount    列数    * @param rowSpacing    行间距    * @param columnSpacing 列间距    */publicGridSpaceItemDecoration(int spanCount, int rowSpacing, int columnSpacing) {this.mSpanCount=spanCount;this.mRowSpacing=rowSpacing;this.mColumnSpacing=columnSpacing;    }@OverridepublicvoidgetItemOffsets(@NonNullRect outRect,@NonNullView view,@NonNullRecyclerView parent,@NonNullRecyclerView.State state) {intposition=parent.getChildAdapterPosition(view);// 获取view 在adapter中的位置。intcolumn=position%mSpanCount;// view 所在的列// 列间距outRect.left=mColumnSpacing;// 如果position > 行数,说明不是在第一行,则不指定行高,其他行的上间距为 top=mRowSpacingif(position >= mSpanCount) {outRect.top=mRowSpacing;// item top        }    }}

添加ItemDecoration

kotlin

复制代码

// 添加间距rvPhotoAlbums.addItemDecoration(new GridSpaceItemDecoration(4,DpPxSpTool.INSTANCE.dip2px(this,30),DpPxSpTool.INSTANCE.dip2px(this,20)));

ok,我们看下效果:

(注:为了区分item view范围,我用不同的颜色描边来识别) 可以看出,item 整体偏右,这是因为GridLayoutManager 已经给item 划分了宽度,而我们在ItemDecoration 中给item view设置了左边距,所以会出现整体偏右,内容挤压的情况。

等分间距

实现思路

为了达到同等间距,我们需要同时设置item 的左右边距,并且使 左边view 的边距+ 右边view 的左边距 = 设置的列间距,从视觉效果上看起来是同等分间距。

先来梳理下等间距需要满足的条件:

各个模块的大小相等,即各列的left+right 值相等;

各列的间距相等,即前列的right + 后列的left = 列间距;

假设列间距为10,为了方便识别,我用字母代替所在列。 以2列为例:

检查条件1 :a.left +a.right = b.left+b.right = 5 结果:满足 检查条件2:a.right + b.left = 10 结果:满足

以3列为例:

检查条件1 :a.left +a.right = b.left+b.right = c.left+c.right ≈ 6.66 结果:满足 检查条件2:a.right + b.left = b.right + c.left ≈ 10 结果:满足

根据推演我们可以得出公式: 某列的left = 所在的列数 * (列间距 * (1 / 列数)) 某列的right = 列间距 - 后列的left = 列间距 -(所在的列数+1) * (列间距 * (1 / 列数)) 注:这里用的所在列数为从0开始

最终实现

less

复制代码

/** * 描述 : RecyclerView GridLayoutManager 等间距。 * <p> * 等间距需满足两个条件: * 1.各个模块的大小相等,即 各列的left+right 值相等; * 2.各列的间距相等,即 前列的right + 后列的left = 列间距; * <p> * 在{@link #getItemOffsets(Rect, View, RecyclerView, RecyclerView.State)} 中针对 outRect 的left 和right 满足这两个条件即可 * <p> * 作者 : shiguotao * 版本 : V1 * 创建时间 : 2020/3/19 4:54 PM */publicclassGridSpaceItemDecorationextendsRecyclerView.ItemDecoration{privatefinalStringTAG= "GridSpaceItemDecoration";privateintmSpanCount;//横条目数量privateintmRowSpacing;//行间距privateintmColumnSpacing;// 列间距/**    * @param spanCount    列数    * @param rowSpacing    行间距    * @param columnSpacing 列间距    */publicGridSpaceItemDecoration(int spanCount, int rowSpacing, int columnSpacing) {this.mSpanCount=spanCount;this.mRowSpacing=rowSpacing;this.mColumnSpacing=columnSpacing;    }@OverridepublicvoidgetItemOffsets(@NonNullRect outRect,@NonNullView view,@NonNullRecyclerView parent,@NonNullRecyclerView.State state) {intposition=parent.getChildAdapterPosition(view);// 获取view 在adapter中的位置。intcolumn=position%mSpanCount;// view 所在的列outRect.left=column*mColumnSpacing/mSpanCount;// column * (列间距 * (1f / 列数))outRect.right=mColumnSpacing-(column +1) *mColumnSpacing/mSpanCount;// 列间距 - (column + 1) * (列间距 * (1f /列数))Log.e(TAG,"position:"+ position+"    columnIndex: "+ column+"    left,right ->"+ outRect.left +","+ outRect.right);// 如果position > 行数,说明不是在第一行,则不指定行高,其他行的上间距为 top=mRowSpacingif(position >= mSpanCount) {outRect.top=mRowSpacing;// item top        }    }}

效果:

打完,收工!

上一篇下一篇

猜你喜欢

热点阅读