ItemDecoration解析(二) onDraw onDrawOver

上篇文章介紹了利用ItemDecorationgetItemOffsets方法來設(shè)置每個ItemView的間隔,這篇文章繼續(xù)介紹下ItemDecoration剩下的兩個方法(未過時的)——onDrawonDrawOver。

在官方的開發(fā)文檔中有指出,onDraw是在itemview繪制之前,onDrawOver是在itemview繪制之后。

All ItemDecorations are drawn in the order they were added, before the item views (in onDraw() and after the items (in onDrawOver(Canvas, RecyclerView, RecyclerView.State).

相信稍微了解過Android中View的繪制流程的都知道,View先會調(diào)用draw方法,在draw中又會調(diào)用onDraw方法。 而在RecyclerViewdraw方法中會先通過super.draw() 調(diào)用父類也就是Viewdraw方法,進而繼續(xù)調(diào)用RecyclerViewOnDraw方法,ItemDecorationsonDraw方法就在此時會被調(diào)用,RecyclerView執(zhí)行完super.draw()之后,ItemDecorationsonDrawOver方法也被調(diào)用,這也就解釋了為什么說onDraw會繪制在itemview之前,表現(xiàn)形式是在最底層(抽象的說法,最底層應(yīng)該是background),onDrawOver是在itemview繪制之后,表現(xiàn)形式在最上層。如果你覺得有點繞的話,可以看看下面的部分源碼:

    /**
     * RecyclerView的draw方法
     * @param c
     */
    @Override
    public void draw(Canvas c) {
        // 調(diào)用父類也就是View的draw方法
        super.draw(c);
        final int count = mItemDecorations.size();
        for (int i = 0; i < count; i++) {
            // 執(zhí)行ItemDecorations的onDrawOver方法
            mItemDecorations.get(i).onDrawOver(c, this, mState);
        }
    }

    /**
     *  View的draw方法
     * @param canvas
     */
    @CallSuper
    public void draw(Canvas canvas) {
        ....
        // View會繼續(xù)調(diào)用onDraw
        if (!dirtyOpaque) onDraw(canvas);
        ....
    }

    /**
     * RecyclerView的onDraw方法
     * @param c
     */
    @Override
    public void onDraw(Canvas c) {
        super.onDraw(c);
        final int count = mItemDecorations.size();
        for (int i = 0; i < count; i++) {
            // 執(zhí)行ItemDecorations的onDraw方法
            mItemDecorations.get(i).onDraw(c, this, mState);
        }
    }

通過上面的分析,我們大概的了解了它的原理,接下來再來看看怎么用吧。

一般情況下,一個列表中的item除了上篇文章中有間隔那種表現(xiàn)形式,還有另一種帶分割線的表現(xiàn)是形式。如下圖所示:

之前在使用ListView的時候,可以通過在xml中定義中divider dividerHeight來實現(xiàn),不過RecyclerView并沒有這些屬性,在未使用ItemDecoration時,一般都是在Itemxml布局的底部設(shè)置一個View來充當Divider,然后在ViewHolder根據(jù)position來控制,這樣同樣顯得ViewHolder中有很一些看上去很不爽的代碼。

那么問題來了,用ItemDecoration怎么實現(xiàn)呢?其實很簡單,只需要在上篇文章的基礎(chǔ)上,把間隔處繪制相應(yīng)顏色就行了,廢話不多說,我們來實現(xiàn)吧。

  1. 通過構(gòu)造方法傳入divider的高度(orientationHORIZONTAL時表示寬度),還有顏色。

        public DividerDecoration(int height, Context ctx) {
            this(height, Color.GRAY, ctx);
        }
    
    
        public DividerDecoration(int height, @ColorInt int color, Context ctx) {
            this.mDividerHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, height, ctx.getResources().getDisplayMetrics());
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setColor(color);
        }
    
  2. 設(shè)置每個Item間的間隔,留出空間畫divider

        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
            if (layoutManager instanceof LinearLayoutManager) {
                if (((LinearLayoutManager) layoutManager).getOrientation() == LinearLayoutManager.HORIZONTAL) {
                    if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1)
                        outRect.set(0, 0, (int) mDividerHeight, 0);
                } else {
                    if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1)
                        outRect.set(0, 0, 0, (int) mDividerHeight);
                }
            }
        }
    
  3. divider

        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            RecyclerView.LayoutManager manager = parent.getLayoutManager();
            if (manager instanceof LinearLayoutManager) {
                if (((LinearLayoutManager) manager).getOrientation() == LinearLayoutManager.VERTICAL) {
                    drawVertical(c, parent);
                } else {
                    drawHorizontal(c, parent);
                }
            }
        }
    
        /**
         * 畫divider (orientation為vertical)
         *
         * @param c
         * @param parent
         */
        private void drawVertical(Canvas c, RecyclerView parent) {
            // recyclerView是否設(shè)置了paddingLeft和paddingRight
            final int left = parent.getPaddingLeft();
            final int right = parent.getWidth() - parent.getPaddingRight();
            final int childCount = parent.getChildCount();
            for (int i = 0; i < childCount; i++) {
                final View child = parent.getChildAt(i);
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                        .getLayoutParams();
                // divider的top 應(yīng)該是 item的bottom 加上 marginBottom 再加上 Y方向上的位移
                final int top = child.getBottom() + params.bottomMargin +
                        Math.round(ViewCompat.getTranslationY(child));
                // divider的bottom就是top加上divider的高度了
                final int bottom = (int) (top + mDividerHeight);
                c.drawRect(left, top, right, bottom, mPaint);
            }
        }
    
        /**
         * 畫divider (當orientation為horizontal)
         *
         * @param c
         * @param parent
         */
        private void drawHorizontal(Canvas c, RecyclerView parent) {
            // 和drawVertical差不多 left right 與 top和bottom對調(diào)一下
            final int top = parent.getPaddingTop();
            final int bottom = parent.getHeight() - parent.getPaddingBottom();
            final int childCount = parent.getChildCount();
            for (int i = 0; i < childCount; i++) {
                final View child = parent.getChildAt(i);
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                        .getLayoutParams();
                final int left = child.getRight() + params.rightMargin +
                        Math.round(ViewCompat.getTranslationX(child));
                final int right = (int) (left + mDividerHeight);
                c.drawRect(left, top, right, bottom, mPaint);
            }
        }
    

其實重寫onDraw或者onDrawOver都能實現(xiàn)上圖的效果,因為這里并沒有層級之分,當有其他的需求時,我們只需要記住onDraw在繪制ItemView之前繪制,onDrawOver會在繪制ItemView之后繪制,然后根據(jù)實際情況處理就行了。下一篇,我們再用ItemDecoration實現(xiàn)更好玩的——stickHeader的效果,先來個圖,預(yù)告下。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 特別聲明: 本文轉(zhuǎn)發(fā)自:【江清清的博客】http://blog.csdn.net/developer_jiangq...
    _猜火車_閱讀 37,831評論 11 70
  • 這篇文章分三個部分,簡單跟大家講一下 RecyclerView 的常用方法與奇葩用法;工作原理與ListView比...
    LucasAdam閱讀 4,698評論 0 27
  • LinearLayoutManager情況 public class DividerItemDecoration ...
    43d60efa37c7閱讀 802評論 0 0
  • 梧桐疏影,月入窗。 隔簾猶聽,風如訴。 重掩門,幕更深, 寄與誰聽? 歲已窮,芳菲暮, 月華樓滿,流年虛度, 驚醒...
    玉生煙閱讀 417評論 0 4
  • 開始認真的去喜歡一個人,認真的去做一件事情
    compete閱讀 230評論 0 0

友情鏈接更多精彩內(nèi)容