ItemDecoration学习

Recycler.ItemDecoration

最近任务遇到了需要在RecyclerView中加入分割线的需求,所以尽可能的说一下对ItemDecoration的理解,之后还酷炫操作的时候再进行补充

简介

ItemDecoration根据官方文档所说,一个ItemDecoration可以允许应用给Adapter数据集中来的特殊列表项Views加特效和布局偏移,这对于在列表项间加分割线,高亮显示,可视化分组都很有用。
我的理解就是加特技,给列表项加特技,一个个加的过程中如果加相同的特技,就起到分隔的作用;如果这几个加一种特技,另外几个加别的特技,就可以起到分组的作用;如果某几个列表项,也就是少量列表项加特技,就可以起到高亮的作用。
特技的本质与canvas类似,由两方面组成:

  • 尺寸
  • 样式

实现

自定义ItemDecoration通过继承Recycler.ItemDecoration实现。

继承需要实现三个方法:

public void onDraw(Canvas c, RecyclerView parent, State state)
public void onDrawOver(Canvas c, RecyclerView parent, State state)
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)

前两个方法比较简单,只需要注意绘制时机就可以,调用顺序为:

graph LR
    A[ItemDecoration.onDraw] --> B[child itemView.OnDraw]
    B --> C[ItemDecoration.onDrawOver]

onDraw

先贴一个DividerItemDecoration中的代码:

private void drawVertical(Canvas canvas, RecyclerView parent) {
        canvas.save();
        final int left;
        final int right;
        //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
        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);
            parent.getDecoratedBoundsWithMargins(child, mBounds);
            final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
            final int top = bottom - mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(canvas);
        }
        canvas.restore();
    }

这个方法容易理解,首先锁定canvas,然后根据RecyclerViewClipToPadding属性确定是否裁剪canvas(默认是裁剪的,可以通过设置为false不进行裁剪),然后对计算每个child的尺寸,根据这个尺寸再计算分割线的尺寸,最后画出分割线。

getItemOffset

比较有意思的是第三个方法getItemOffsets,只在这里被调用:

Rect getItemDecorInsetsForChild(View child) {
        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
        if (!lp.mInsetsDirty) {
            return lp.mDecorInsets;
        }

        final Rect insets = lp.mDecorInsets;
        insets.set(0, 0, 0, 0);
        final int decorCount = mItemDecorations.size();
        for (int i = 0; i < decorCount; i++) {
            mTempRect.set(0, 0, 0, 0);
            mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
            insets.left += mTempRect.left;
            insets.top += mTempRect.top;
            insets.right += mTempRect.right;
            insets.bottom += mTempRect.bottom;
        }
        lp.mInsetsDirty = false;
        return insets;
}

方法返回一个Rect,这个Rect通过遍历ItemDecorations列表把每个ItemDecoration的左上右下边距值累加获得。getItemOffsets方法的作用就是获取边距Rect
解释下chlid.getLayoutParams().mInsetsDirty这个是一个缓冲机制,用来避免重复计算边距。

而这个getItemDecorInsetsForChild调用在:

public void measureChild(View child, int widthUsed, int heightUsed) {
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
            widthUsed += insets.left + insets.right;
            heightUsed += insets.top + insets.bottom;

            final int widthSpec = getChildMeasureSpec(getWidth(),
                    getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
                    canScrollHorizontally());
            final int heightSpec = getChildMeasureSpec(getHeight(),
                    getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
                    canScrollVertically());
            child.measure(widthSpec, heightSpec);
        }

可以看到这个方法用来将ItemDecoration的边距值放在child的MeaSureSpecpadding

Demo

class TopicDecoration extends RecyclerView.ItemDecoration {

        private final Drawable mDivider;
        private final int mSize;
        private final int mSizeHeader;
        private final int marginHorizontal;

        TopicDecoration(Resources resources, int size, int size2, int width) {
            mDivider = new ColorDrawable(resources.getColor(R.color.colordddddd));
            mSize = size;
            mSizeHeader = size2;
            marginHorizontal = width;
        }

        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            int left;
            int right;
            int top;
            int bottom;
            left = parent.getPaddingLeft() + marginHorizontal;
            right = parent.getWidth() - parent.getPaddingRight() - marginHorizontal;
            final int childCount = parent.getChildCount();
            for (int i = 0; i < childCount - 1; i++) {
                if (i == 0) {
                    int left2 = parent.getPaddingLeft();
                    int right2 = parent.getWidth() - parent.getPaddingRight();
                    final View child = parent.getChildAt(i);
                    final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
                    top = child.getBottom() + params.bottomMargin;
                    bottom = top + mSizeHeader;
                    mDivider.setBounds(left2, top, right2, bottom);
                    mDivider.draw(c);
                } else {
                    final View child = parent.getChildAt(i);
                    final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
                    top = child.getBottom() + params.bottomMargin;
                    bottom = top + mSize;
                    mDivider.setBounds(left, top, right, bottom);
                    mDivider.draw(c);
                }
            }
        }

        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            if (parent.getChildLayoutPosition(view) == 0) {
                outRect.set(0, 0, 0, mSizeHeader);
            } else {
                outRect.set(0, 0, 0, mSize);
            }
        }
    }

你可能感兴趣的:(ItemDecoration学习)