參考資料
參考資料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);
}
這裡借張圖,侵删!

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);
}
借張圖,侵删!
這裡有個坑,在我使用
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礦泉水時(這不是廣告),發現上面裹桶口裹的一層保護塑膠,裹的倒是十分結實!但是我找了半天沒發現有可以下手撕開的地方,也就是說要麼硬摳開,要麼借助工具,總之就是不能讓你輕易就打開。不知道生産這種桶裝水的人自己用不用,或者說自己換過水沒?
這事兒,你怎麼看?