天天看點

Android之RecyclerView——用ItemDecoration裝飾你的Item參考資料背景介紹認識ReccylerView.ItemDecoration總結探讨

參考資料

參考資料1;

背景介紹

RecyclerView由于它強大的靈活性,已經可以代替掉傳統的ListView和GridView等清單控件了。但是也因為它的靈活性,一些東西就沒有固定,需要我們自己來實作,比如RecyclerView就沒有提供預設的分割線,當我們需要一條分割線時,我們需要自己通過繼承RecyclerView.ItemDecoration來實作。當然,它不僅僅可以實作分割線,還能創造很多不一樣的效果,這取決于我們的想象力在這個限制範圍内能到什麼程度了。在另一篇RecyclerView——LayoutManager中,我們有必要先了解一點本篇的内容。接下來我們來好好聊聊ItemDecoration。

認識ReccylerView.ItemDecoration

為RecyclverView添加ItemDecoration

我們可以通過調用:

mRecyclerView.addItemDecoration(mItemDecoration);
           

來為我們的RecyclerView添加一個自定義的ItemDecoration裝飾它。

這裡值得注意的是我們采用的是addXXX方法,這意味着我們可以為RcyclerView添加多個ItemDecoration。打開該方法的源碼我們可以看到:

//這是我們通常調用的代碼
public void addItemDecoration(ItemDecoration decor) {
        //這裡又調用了另一個重載的方法
        addItemDecoration(decor, -);
    }

/**
     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
     * affect both measurement and drawing of individual item views.
     *
     * <p>Item decorations are ordered. Decorations placed earlier in the list will
     * be run/queried/drawn first for their effects on item views. Padding added to views
     * will be nested; a padding added by an earlier decoration will mean further
     * item decorations in the list will be asked to draw/pad within the previous decoration's
     * given area.</p>
     *
     * @param decor Decoration to add
     * @param index Position in the decoration chain to insert this decoration at. If this value
     *              is negative the decoration will be added at the end.
     * 通過文檔我們可以知道,index參數控制這我們add的ItemDecoration添加到mItemDecorations數組中的位置。如果小于0,我們的ItemDecoration将被添加到最後。
     */
public void addItemDecoration(ItemDecoration decor, int index) {
        if (mLayout != null) {
            mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
                    + " layout");
        }
        if (mItemDecorations.isEmpty()) {
            setWillNotDraw(false);
            //注意,當我們首次添加ItemDecoration時,mItemDecorations是一個空的數組,
           //但這事我們就需要讓View開啟自我繪制,否則RecyclerView的onDraw()方法将有可能不被執行。
           //我們在寫自定義ViewGroup時也需要注意這個問題。
        }
        if (index < ) {
        //這個邏輯保證了小于0,添加到末尾
            mItemDecorations.add(decor);
        } else {
        //添加到指定位置
            mItemDecorations.add(index, decor);
        }
        //這個方法最終把RecyclerView的Item的LayoutParams的mInsertDirty屬性設定為true,
        //這樣在measure時,才能夠把所有的ItemDecoration中的itemOffset添加到Item的布局參數上。
        markItemDecorInsetsDirty();
        requestLayout();
    }
           

是以,我們可以多次調用

addItemDecoration()

,并且每次添加的ItemDecoration的itemOffset都将累加到Item的布局參數上。

自定義ItemDecoration

對于自定義ItemDecoration我們通常僅僅關心以下3個方法就行。

getItemOffsets()

這個方法有2個重載方法:

public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
            outRect.set(, , , );
            //注意,重寫時super要慎重使用,否則設定的Rect會被重置。
            //通過上面的講解,我們知道,在這裡設定的outRect的參數最終都會被累加到Item的布局參數上。
            //如果值設為負數,那麼Item會發生重疊。
        }

//這個方法主要能夠友善的擷取到View和目前RecyclerView的狀态,
//但它最終調用的仍然是上一個方法,是以重寫這個方法需要注意super,最好就不要要。
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
            getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
                    parent);
        }
           

這裡借張圖,侵删!

Android之RecyclerView——用ItemDecoration裝飾你的Item參考資料背景介紹認識ReccylerView.ItemDecoration總結探讨

onDraw()

該方法同樣有兩個重載方法:

public void onDraw(Canvas c, RecyclerView parent){
    //在這個方法中利用Canvas繪制點什麼,
    //它最終會繪制在Item的下一層,如果它超出Item,那麼超出部分将可見。
    //注意,計算Item的top時,記得加上Translation,否則不能準确計算
}

//該方法最終還是調用上一個方法,是以要注意super,最好就不要。這個方法能夠擷取到RecyclerView狀态。
public void onDraw(Canvas c, RecyclerView parent, State state) {
            onDraw(c, parent);
        }
           

借張圖,侵删!

Android之RecyclerView——用ItemDecoration裝飾你的Item參考資料背景介紹認識ReccylerView.ItemDecoration總結探讨

這裡有個坑,在我使用

mDrawable.canvas()

來繪制分割線的時候,由于

mDrawable.getIntrinscXXX()

獲得的值為-1,是以導緻了繪制效果不可見。解決辦法是在計算bottom或right時,直接指定我們所希望的間距,除非我們已經設定mDrawable的尺寸。

還有一點需要注意:最終getItemOffset()方法中設定的偏移距離會疊加到Item的布局尺寸參數上,而分割線的繪制應該在Item底部(去尾),或者說Item頂部(去頭),繪制區域

onDrawOver()

onDraw()

方法類似,不同的是它将被覆寫在Item上面。它同樣有兩個重載方法:

public void onDrawOver(Canvas c, RecyclerView parent, State state) {
            onDrawOver(c, parent);
        }


@Deprecated
public void onDrawOver(Canvas c, RecyclerView parent) {
        }
           

總結

通過自定義

ItemDecoration

我們可以實作需要吊炸天的效果。先遵守規則,然後天馬行空。

探讨

今天在換燕x礦泉水時(這不是廣告),發現上面裹桶口裹的一層保護塑膠,裹的倒是十分結實!但是我找了半天沒發現有可以下手撕開的地方,也就是說要麼硬摳開,要麼借助工具,總之就是不能讓你輕易就打開。不知道生産這種桶裝水的人自己用不用,或者說自己換過水沒?

這事兒,你怎麼看?