經過ItemDecoration
,能夠給RecyclerView
或者RecyclerView
中的每一個Item
添加額外的裝飾效果,最經常使用的就是用來爲Item
之間添加分割線,今天,咱們就來一塊兒學習有關的知識:android
API
DividerItemDecoration
解析ItemDecoration
API
介紹當咱們實現本身的ItemDecoration
時,須要繼承於ItemDecoration
,並根據須要實現如下三個方法:canvas
public void onDraw(Canvas c, RecyclerView parent, State state)
canvas
:RecyclerView
的canvas
parent
:RecyclerView
實例State
:RecyclerView
當前的狀態,值包括START/LAYOUT/ANIMATION
。全部在這個方法中的繪製操做,將會在itemViews
被繪製以前執行,所以,它會顯示在itemView
之下。bash
public void onDrawOver(Canvas c, RecyclerView parent, State state)
和2.1
方法相似,區別在於它繪製在itemViews
之上。ide
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)
經過outRect
,能夠設置item
之間的間隔,間隔區域的大小就是outRect
所指定的範圍,view
就是對應位置的itemView
,其它的參數解釋和上面相同。學習
DividerItemDecoration
解析上面咱們解釋了須要重寫的方法以及其中參數的含義,下面,咱們經過官方自帶的DividerItemDecoration
,來進一步加深對這些方法的認識。 DividerItemDecoration
是爲LinearLayoutManager
提供的分割線,在建立它的時候,須要指定ORIENTATION
,這個方向應當和LinearLayoutManager
的方向相同。ui
private void init() {
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_content);
mTitles = new ArrayList<>();
for (int i = 0; i < 20; i++) {
mTitles.add(String.valueOf(i));
}
BaseAdapter baseAdapter = new BaseAdapter(mTitles);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
recyclerView.setAdapter(baseAdapter);
}
複製代碼
最終展現的效果爲: this
DividerItemDecoration
重寫了基類當中的onDraw
方法,也就是說這個分割線是在itemView
以前繪製的:spa
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (parent.getLayoutManager() == null) {
return;
}
if (mOrientation == VERTICAL) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
複製代碼
咱們先看縱向排列的RecyclerView
分割線:.net
@SuppressLint("NewApi")
private void drawVertical(Canvas canvas, RecyclerView parent) {
//首先保存畫布
canvas.save();
final int left;
final int right;
//肯定左右邊界的範圍,若是RecyclerView不容許子View繪製在Padding內,那麼這個範圍爲去掉Padding後的範圍
if (parent.getClipToPadding()) {
left = parent.getPaddingLeft();
right = parent.getWidth() - parent.getPaddingRight();
canvas.clipRect(left, parent.getPaddingTop(), right,
parent.getHeight() - parent.getPaddingBottom());
} else {
left = 0;
right = parent.getWidth();
}
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
//得到itemView的範圍,這個範圍包括了margin和offset,它們被保存在mBounds當中
parent.getDecoratedBoundsWithMargins(child, mBounds);
//須要考慮translationY和translationY
final int bottom = mBounds.bottom + Math.round(ViewCompat.getTranslationY(child));
//因爲是垂直排列的,所以上邊界等於下邊界減去分割線的高度.
final int top = bottom - mDivider.getIntrinsicHeight();
//設置divider和範圍
mDivider.setBounds(left, top, right, bottom);
//繪製.
mDivider.draw(canvas);
}
//回覆畫布.
canvas.restore();
}
複製代碼
整個過程分爲三步:rest
View
在RecyclerView
中的繪製範圍View
的範圍mDivider
的繪製範圍下圖就是最終計算的結果:
橫向排列的RecyclerView
列表和上面的原理是相同的,區別就在於計算
mDivider.setBounds
的計算:
//....
parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);
final int right = mBounds.right + Math.round(ViewCompat.getTranslationX(child));
final int left = right - mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
//..
複製代碼
從上面的分析能夠知道,若是將divider
直接繪製在itemView
的範圍內,那麼因爲咱們是先繪製divider
,再繪製itemView
的內容的,那麼它就會被覆蓋,所以,經過重寫getItemOffsets
,經過其中的outRect
來指定留出的空隙:
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
if (mOrientation == VERTICAL) {
//若是是縱向排列,那麼要在itemView的下方留出一個下邊界
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
//若是是橫向排列,那麼要在itemView的右方留出一個右邊界
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
複製代碼
ItemDecoration
下面,咱們參考上面的寫法,寫一個簡單的GridLayoutManager
的分割線:
public class GridDividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[] { android.R.attr.listDivider };
private Drawable mDivider;
private final Rect mBounds = new Rect();
public GridDividerItemDecoration(Context context) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
}
public void setDrawable(@NonNull Drawable drawable) {
mDivider = drawable;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
drawDivider(c, parent);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight());
}
private void drawDivider(Canvas canvas, RecyclerView parent) {
canvas.save();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View view = parent.getChildAt(i);
parent.getDecoratedBoundsWithMargins(view, mBounds);
mDivider.setBounds(mBounds.right - mDivider.getIntrinsicWidth(), mBounds.top, mBounds.right, mBounds.bottom);
mDivider.draw(canvas);
mDivider.setBounds(mBounds.left, mBounds.bottom - mDivider.getIntrinsicHeight(), mBounds.right , mBounds.bottom);
mDivider.draw(canvas);
}
canvas.restore();
}
}
複製代碼
最終的效果爲:
這裏咱們爲了演示方便,沒有考慮最後一列或者最後一行沒有分割線的狀況,這篇文章寫的比較好: Android RecyclerView 使用徹底解析 體驗藝術般的控件。ItemDecoration
的使用並不難,大多數狀況下就只須要重寫onDraw
和onDrawOver
中的一個;若是須要在Item
之間添加間隔,那麼要重寫getItemOffsets
並理解outRect
的含義,假如不須要添加間隔,那麼不須要重寫該方法。