美文网首页
ItemDecoration学习

ItemDecoration学习

作者: 噜噜1231 | 来源:发表于2018-08-02 19:43 被阅读0次

    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学习

          本文链接:https://www.haomeiwen.com/subject/zfgsvftx.html