一、 摘要
本文介紹使用RecyclerView的抽象內部類ItemDecoration實現ItemView分割線的繪製。
二、 方法分析
實現分割線的繪製,需要重寫兩個方法:getItemOffsets()和onDraw()。
1. ItemDecoration.getItemOffsets
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state)
看方法名,獲取item的偏移量,outRect
是item的偏移量矩形區域,對其設置四個方向的值,便起到設置該item在四個方向上的偏移量的作用,這個偏移量的實際效果,是擴展item本身的區域,例如:
outRect.set(0, 0, 0, 1);
該代碼的效果是讓這個item的bottom位置空出1dp的高度。
獲取當前item對應位置的方法:
int position = parent.getChildAdapterPosition(view);
2. ItemDecoration.onDraw
public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state)
這個方法就是具體的繪製操作了,該方法中,我們應該和上一個方法保持一致,在位置相對應時纔去繪製canvas,我們需要通過遍歷parent
來獲取每個item對應的位置:
for (int i = 0; i < parent.getChildCount(); i++) {
View view = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(view);
// TODO draw canvas
}
3. RecyclerView.addItemDecoration
public void addItemDecoration(@NonNull ItemDecoration decor)
對RecyclerView添加一個Item裝飾器
4. RecyclerView.invalidateItemDecorations
public void invalidateItemDecorations()
刷新item裝飾器,該方法內部會調用裝飾器的getItemOffsets()和onDraw()方法。
三、 示例
假設現在我們的需求是:有一個垂直方向的RecyclerView,在指定位置item的底部加上一條1dp高的分割線:
先自定義一個ItemDecoration:
public class DemoItemDecoration extends RecyclerView.ItemDecoration {
private final static int DIVIDE_HEIGHT = 1;
private Context mContext;
private Paint mPaint;
private int mDividerPosition;
public DemoItemDecoration(Context context) {
mContext = context;
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
}
public void setPosition(int position) {
mDividerPosition = position;
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int position = parent.getChildAdapterPosition(view);
if (position == mDividerPosition) {
outRect.set(0, 0, 0, dip2px(DIVIDE_HEIGHT));
}
}
@Override
public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDraw(canvas, parent, state);
for (int i = 0; i < parent.getChildCount(); i++) {
View view = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(view);
if (position == mDividerPosition) {
drawDivider(canvas, view);
return;
}
}
}
/**
* 在{@link #getItemOffsets}中,我們已經讓item底部空出了1dp的位置,現在我們要在這1dp高的區域內填充,實現分割線
*
* @param canvas
* @param view
*/
private void drawDivider(Canvas canvas, View view) {
int left = view.getLeft();
int right = view.getRight();
int top = view.getBottom();
int bottom = top + dip2px(DIVIDE_HEIGHT);
canvas.drawRect(left, top, right, bottom, mPaint);
}
private int dip2px(int dip) {
float density = mContext.getResources().getDisplayMetrics().density;
return (int) (dip * density + 0.5f);
}
}
然後將這個裝飾器應用於RecyclerView上:
private RecyclerView mRecyclerView;
private DemoItemDecoration mItemDecoration;
private void initView(){
mRecyclerView = findViewById(R.id.rvDemo);
mItemDecoration = new DemoItemDecoration(this);
mRecyclerView.addItemDecoration(mItemDecoration);
}
private void updateView(int position){
mItemDecoration.setPosition(position);
mRecyclerView.invalidateItemDecorations();
}
四、 總結
在學會使用Item裝飾器之前,我們想實現在RecyclerView中插入分割線,可能得定義一個ViewHolder,然後在Adapter中進行適配,如今我們可以利用裝飾器更加優雅地實現這個功能。同理,我們可以在Item的頂部加上組名進行分組,比如實現一個聯繫人列表,組名是大寫首字母。