自定义RecyclerView的ItemDecoration,用于处理网格布局间距装饰器(支持边缘间距独立控制)
自定义RecyclerView的ItemDecoration,用于处理网格布局的间距,网格布局间距装饰器(支持边缘间距独立控制),为RecyclerView网格布局提供灵活的间距控制,可单独设置各边缘间距。
核心功能说明:
- 灵活的边缘控制:可单独设置首列/末列/首行/末行的间距
- 智能间距分配:
- 多列布局时自动将间距平分到相邻item两侧
- 单列布局时直接应用指定间距
3.两种边缘模式:
- includeEdge=true:默认包含顶部/底部边缘间距
- includeEdge=false:不包含默认边缘间距(可单独设置)
4.辅助计算功能:提供getTotalWidthNeeded()方法预计算item宽度
如:GridLayoutManager 设计一个一行4列的,且每列的间距都是10dp
// 在Activity或Fragment中
RecyclerView recyclerView = findViewById(R.id.recyclerView);// 设置GridLayoutManager,4列
GridLayoutManager layoutManager = new GridLayoutManager(this, 4);
recyclerView.setLayoutManager(layoutManager);// 设置ItemDecoration来添加间距
int spacingInPixels = getResources().getDimensionPixelSize(R.dimen.grid_spacing);// 移除旧的 ItemDecoration(避免重复添加)if (recyclerView.getItemDecorationCount() > 0) {recyclerView.removeItemDecorationAt(0);}// 添加自定义边距recyclerView.addItemDecoration(new GridSpacingItemDecoration(4,spacingInPixels ,true,spacingInPixels,spacingInPixels,0,spacingInPixels));// 设置适配器
ImageAdapter adapter = new ImageAdapter(imageList); // 假设有适配器
recyclerView.setAdapter(adapter);
GridSpacingItemDecoration.java 代码:
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;/*** 网格布局间距装饰器(支持边缘间距独立控制)* 功能:为RecyclerView网格布局提供灵活的间距控制,可单独设置各边缘间距*/
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {private final int spanCount; // 网格列数private final int spacing; // 默认间距大小(像素)private final boolean includeEdge; // 是否包含边缘间距(影响顶部/底部默认行为)// 以下参数为可选边缘间距控制(-1表示使用默认间距)private int includeFirstColumnSpacing = -1; // 第一列左边距(可覆盖默认值)private int includeLastColumnSpacing = -1; // 最后一列右边距(可覆盖默认值)private int includeFirstRowTopSpacing = -1; // 第一行上边距(可覆盖默认值)private int includeLastRowBottomSpacing = -1; // 最后一行下边距(可覆盖默认值)/*** 全参数构造函数* * @param spanCount 网格列数(必须大于0)* @param spacing 默认间距值(像素)* @param includeEdge 是否包含边缘间距(影响顶部/底部默认行为)* @param includeFirstColumnSpacing 第一列左边距(-1表示使用默认间距)* @param includeLastColumnSpacing 最后一列右边距(-1表示使用默认间距)* @param includeFirstRowTopSpacing 第一行上边距(-1表示使用默认间距)* @param includeLastRowBottomSpacing 最后一行下边距(-1表示使用默认间距)*/public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge,int includeFirstColumnSpacing,int includeLastColumnSpacing,int includeFirstRowTopSpacing,int includeLastRowBottomSpacing) {this.spanCount = spanCount;this.spacing = spacing;this.includeEdge = includeEdge;this.includeFirstColumnSpacing = includeFirstColumnSpacing;this.includeLastColumnSpacing = includeLastColumnSpacing;this.includeFirstRowTopSpacing = includeFirstRowTopSpacing;this.includeLastRowBottomSpacing = includeLastRowBottomSpacing;}@Overridepublic void getItemOffsets(Rect outRect, View view, RecyclerView parent,RecyclerView.State state) {int position = parent.getChildAdapterPosition(view); // 当前item位置int column = position % spanCount; // 当前item所在列(0~spanCount-1)int totalItemCount = parent.getAdapter().getItemCount(); // 总item数int totalRows = (int) Math.ceil((double) totalItemCount / spanCount); // 计算总行数int currentRow = position / spanCount + 1; // 当前行号(从1开始)/* ========== 1. 处理左右边距 ========== */if (spanCount == 1) {// 单列特殊情况处理outRect.left = getEffectiveSpacing(includeFirstColumnSpacing, spacing);outRect.right = getEffectiveSpacing(includeLastColumnSpacing, spacing);} else {// 多列常规处理if (column == 0) {// 第一列:左间距可定制,右间距为默认一半outRect.left = getEffectiveSpacing(includeFirstColumnSpacing, spacing);outRect.right = spacing / 2;} else if (column == spanCount - 1) {// 最后一列:左间距为默认一半,右间距可定制outRect.left = spacing / 2;outRect.right = getEffectiveSpacing(includeLastColumnSpacing, spacing);} else {// 中间列:左右间距均为默认一半(实现等间距分布)outRect.left = spacing / 2;outRect.right = spacing / 2;}}/* ========== 2. 处理上下边距 ========== */if (includeEdge) {// 包含边缘模式if (currentRow == 1) {// 第一行顶部间距outRect.top = getEffectiveSpacing(includeFirstRowTopSpacing, spacing);}// 底部间距处理if (currentRow == totalRows) {// 最后一行底部间距可定制outRect.bottom = getEffectiveSpacing(includeLastRowBottomSpacing, spacing);} else if (currentRow < totalRows) {// 中间行固定底部间距outRect.bottom = spacing;}} else {// 不包含边缘模式outRect.top = currentRow == 1 ? getEffectiveSpacing(includeFirstRowTopSpacing, spacing) : spacing;// 最后一行特殊处理if (currentRow == totalRows) {outRect.bottom = getEffectiveSpacing(includeLastRowBottomSpacing, spacing);}}}/*** 获取有效间距值* @param customSpacing 自定义间距值(-1表示使用默认值)* @param defaultSpacing 默认间距值* @return 实际使用的间距值*/private int getEffectiveSpacing(int customSpacing, int defaultSpacing) {return customSpacing > -1 ? customSpacing : defaultSpacing;}/*** 计算RecyclerView需要的总宽度(用于预计算item宽度)* @param screenWidth 屏幕可用宽度* @return 单个item的理论宽度*/public int getTotalWidthNeeded(int screenWidth) {if (spanCount == 0) return screenWidth;// 计算总间距 = 列间间距 + 边缘间距int totalSpacing = (spanCount > 1) ? (spanCount - 1) * spacing : 0;if (includeFirstColumnSpacing > 0) totalSpacing += spacing;if (includeLastColumnSpacing > 0) totalSpacing += spacing;// 返回单个item宽度 = (总宽度 - 总间距) / 列数return (screenWidth - totalSpacing) / spanCount;}
}