前言
在项目中,我们经常使用RecyclerView来实现宫格布局(比如上传多张照片),我们会使用GridLayoutManger这种布局管理器配合ItemDecoration来实现。
可是有时不正当的ItemDecoration的写法会导致布局出现问题,比如,我们在实现如下界面时:
根据设计稿来看,这个九宫格一行有3张图,每行最左边和最右边跟卡片的间距先不管,每行或每列每2张图之间间距为12dp,图片大小为87dp x 87dp。如果我们照“正常”思路这样写ItemDecoration,效果就会变成下图这样。
错误示例
//仅给出关键代码
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//获得recyclerView的布局,需要转化
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
int position = layoutParams.getViewAdapterPosition();
//获得view在adapter的位置
if (position % 3 == 0)
outRect.right = dp2px(6);
else if (position % 3 == 1)
{
outRect.left = dp2px(6);
outRect.right = dp2px(6);
}
else
outRect.left = dp2px(6);
if (position > 2)
outRect.top = dp2px(12);
}
解决方案
如上面的效果图和代码,可以看出设置间隔后,左右2张图把中间图片的宽度分割了,导致中间的图片会显得比较“消瘦”。其实乍一看以上代码重写的getItemOffsets没什么问题,但是getItemOffsets方法中,每个item中outRect的left和right如果加起来不一样大,配合GridLayoutManger必然会出现里面item的宽度不对的问题。
那么我们该怎么写ItemDecoration呢,因为我们效果图是只有中间的图和两边的图有相等的间距,所以我们可以依旧使用getItemOffsets方法,但是要对其left和right作出一定的补偿,如下代码。
/**
* 作者:HK
* 日期:2019/5/5
* 描述:宫格布局的间隔处理
*
* 横向:中间有等距间隔(左右两边与屏幕边沿的间隔另行处理);
* 纵向:第二行往下开始,有顶部的间隔(且每行间隔都相等)
*/
public class GridItemDecoration extends RecyclerView.ItemDecoration {
private Context mContext;
private int spanCount;
private int dividerWidth;
/**
* @param spanCount gridLayoutManager 列数
* @param dividerWidthDp 分割块的宽/高,单位:dp(m值,已适配)
*/
public GridItemDecoration(Context context, int spanCount, int dividerWidthDp) {
this.mContext = context;
this.spanCount = spanCount;
this.dividerWidth = dp2px(dividerWidthDp);
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View child, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, child, parent, state);
int pos = parent.getChildAdapterPosition(child);
// 计算这个child 处于第几列
int column = (pos) % spanCount;
outRect.left = (column * dividerWidth / spanCount);
outRect.right = dividerWidth - (column + 1) * dividerWidth / spanCount;
if (pos >= spanCount)
outRect.top = dividerWidth;
}
private int dp2px(int values) {
return mContext.getResources().getDimensionPixelSize(ResourcesUtils.getDimen(
"m" + values, mContext));
}
}
//附上使用方式
private void setAdapter(RecyclerView rv)
{
...
rv.addItemDecoration(new GridItemDecoration(mContext, 3, 12));
GridLayoutManager layout = new GridLayoutManager(mContext, 3);
rv.setLayoutManager(layout);
...
}
item的布局,注意宽度要使用match_parent,别写死,GridLayoutManger会自动均分的
<?xml version="1.0" encoding="utf-8"?>
<com.beautyperi.view.RoundImageView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/iv_icon"
android:layout_width="match_parent"
android:layout_height="@dimen/m87"
android:scaleType="centerCrop"
app:roundHeight="@dimen/m4"
app:roundWidth="@dimen/m4" />