ItemDecoration 是 RecyclerView 组件的一个非常有用的功能,用于添加自定义的装饰项(如分隔线、边距、背景等)到 RecyclerView 的每个 item 之间或周围。
recyclerView.addItemDecoration()
ItemDecoration主要的三个方法:
图片
实现分割线效果需要 getItemOffsets()和 onDraw()2个方法,首先用 getItemOffsets给item下方空出一定高度的空间(例子中是1dp),然后用onDraw绘制这个空间。
public class SimpleDividerDecoration extends RecyclerView.ItemDecoration { private int dividerHeight; private Paint dividerPaint; public SimpleDividerDecoration(Context context) { dividerPaint = new Paint(); dividerPaint.setColor(context.getResources().getColor(R.color.colorAccent)); dividerHeight = context.getResources().getDimensionPixelSize(R.dimen.divider_height); } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); outRect.bottom = dividerHeight; } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { int childCount = parent.getChildCount(); int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); for (int i = 0; i < childCount - 1; i++) { View view = parent.getChildAt(i); float top = view.getBottom(); float bottom = view.getBottom() + dividerHeight; c.drawRect(left, top, right, bottom, dividerPaint); } }}
图片
标签都是覆盖在内容之上的,可以用onDrawOver()来实现,这里简单实现一个颜色标签。
public class LeftAndRightTagDecoration extends RecyclerView.ItemDecoration { private int tagWidth; private Paint leftPaint; private Paint rightPaint; public LeftAndRightTagDecoration(Context context) { leftPaint = new Paint(); leftPaint.setColor(context.getResources().getColor(R.color.colorAccent)); rightPaint = new Paint(); rightPaint.setColor(context.getResources().getColor(R.color.colorPrimary)); tagWidth = context.getResources().getDimensionPixelSize(R.dimen.tag_width); } @Override public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDrawOver(c, parent, state); int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { View child = parent.getChildAt(i); int pos = parent.getChildAdapterPosition(child); boolean isLeft = pos % 2 == 0; if (isLeft) { float left = child.getLeft(); float right = left + tagWidth; float top = child.getTop(); float bottom = child.getBottom(); c.drawRect(left, top, right, bottom, leftPaint); } else { float right = child.getRight(); float left = right - tagWidth; float top = child.getTop(); float bottom = child.getBottom(); c.drawRect(left, top, right, bottom, rightPaint); } } }}
图片
ItemDecoration是可以叠加的,可以将多个效果通过addItemDecoration方法叠加,将上面两种效果叠加。
recyclerView.addItemDecoration(new LeftAndRightTagDecoration(this));recyclerView.addItemDecoration(new SimpleDividerDecoration(this));
图片
定义接口用来进行数据分组和获取首字母,重写getItemOffsets()和onDraw()方法,并根据数据进行分组处理。
public interface DecorationCallback { long getGroupId(int position); String getGroupFirstLine(int position); }
public class SectionDecoration extends RecyclerView.ItemDecoration { private static final String TAG = "SectionDecoration"; private DecorationCallback callback; private TextPaint textPaint; private Paint paint; private int topGap; private Paint.FontMetrics fontMetrics; public SectionDecoration(Context context, DecorationCallback decorationCallback) { Resources res = context.getResources(); this.callback = decorationCallback; paint = new Paint(); paint.setColor(res.getColor(R.color.colorAccent)); textPaint = new TextPaint(); textPaint.setTypeface(Typeface.DEFAULT_BOLD); textPaint.setAntiAlias(true); textPaint.setTextSize(80); textPaint.setColor(Color.BLACK); textPaint.getFontMetrics(fontMetrics); textPaint.setTextAlign(Paint.Align.LEFT); fontMetrics = new Paint.FontMetrics(); topGap = res.getDimensionPixelSize(R.dimen.sectioned_top);//32dp } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); int pos = parent.getChildAdapterPosition(view); Log.i(TAG, "getItemOffsets:" + pos); long groupId = callback.getGroupId(pos); if (groupId < 0) return; if (pos == 0 || isFirstInGroup(pos)) {//同组的第一个才添加padding outRect.top = topGap; } else { outRect.top = 0; } } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDraw(c, parent, state); int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { View view = parent.getChildAt(i); int position = parent.getChildAdapterPosition(view); long groupId = callback.getGroupId(position); if (groupId < 0) return; String textLine = callback.getGroupFirstLine(position).toUpperCase(); if (position == 0 || isFirstInGroup(position)) { float top = view.getTop() - topGap; float bottom = view.getTop(); c.drawRect(left, top, right, bottom, paint);//绘制红色矩形 c.drawText(textLine, left, bottom, textPaint);//绘制文本 } } } private boolean isFirstInGroup(int pos) { if (pos == 0) { return true; } else { long prevGroupId = callback.getGroupId(pos - 1); long groupId = callback.getGroupId(pos); return prevGroupId != groupId; } } public interface DecorationCallback { long getGroupId(int position); String getGroupFirstLine(int position); }}
recyclerView.addItemDecoration(new SectionDecoration(this, new SectionDecoration.DecorationCallback() { @Override public long getGroupId(int position) { return Character.toUpperCase(dataList.get(position).getName().charAt(0)); } @Override public String getGroupFirstLine(int position) { return dataList.get(position).getName().substring(0, 1).toUpperCase(); }}));
头部吸顶效果,header不动肯定是要绘制item内容之上,需要重写onDrawOver()方法,其和Section实现一样。
public class PinnedSectionDecoration extends RecyclerView.ItemDecoration { private static final String TAG = "PinnedSectionDecoration"; private DecorationCallback callback; private TextPaint textPaint; private Paint paint; private int topGap; private Paint.FontMetrics fontMetrics; public PinnedSectionDecoration(Context context, DecorationCallback decorationCallback) { Resources res = context.getResources(); this.callback = decorationCallback; paint = new Paint(); paint.setColor(res.getColor(R.color.colorAccent)); textPaint = new TextPaint(); textPaint.setTypeface(Typeface.DEFAULT_BOLD); textPaint.setAntiAlias(true); textPaint.setTextSize(80); textPaint.setColor(Color.BLACK); textPaint.getFontMetrics(fontMetrics); textPaint.setTextAlign(Paint.Align.LEFT); fontMetrics = new Paint.FontMetrics(); topGap = res.getDimensionPixelSize(R.dimen.sectioned_top); } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); int pos = parent.getChildAdapterPosition(view); long groupId = callback.getGroupId(pos); if (groupId < 0) return; if (pos == 0 || isFirstInGroup(pos)) { outRect.top = topGap; } else { outRect.top = 0; } } @Override public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDrawOver(c, parent, state); int itemCount = state.getItemCount(); int childCount = parent.getChildCount(); int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); float lineHeight = textPaint.getTextSize() + fontMetrics.descent; long preGroupId, groupId = -1; for (int i = 0; i < childCount; i++) { View view = parent.getChildAt(i); int position = parent.getChildAdapterPosition(view); preGroupId = groupId; groupId = callback.getGroupId(position); if (groupId < 0 || groupId == preGroupId) continue; String textLine = callback.getGroupFirstLine(position).toUpperCase(); if (TextUtils.isEmpty(textLine)) continue; int viewBottom = view.getBottom(); float textY = Math.max(topGap, view.getTop()); if (position + 1 < itemCount) { //下一个和当前不一样移动当前 long nextGroupId = callback.getGroupId(position + 1); if (nextGroupId != groupId && viewBottom < textY ) {//组内最后一个view进入了header textY = viewBottom; } } c.drawRect(left, textY - topGap, right, textY, paint); c.drawText(textLine, left, textY, textPaint); } }}
图片
本文链接:http://www.28at.com/showinfo-26-87958-0.htmlRecyclerView中ItemDecoration的精妙用法,实现自定义分隔线、边距和背景效果
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com