之前用 ItemDecoration 一直都是用的源碼裏唯一附帶的 DividerItemDecoration。 或者直接在每個Item裏寫分割線代碼了。
一直沒怎麼管 ItemDecoration。
這段時間因爲需要用到所以學習了下用法。
這個類需要繼承 RecyclerView。ItemDecoration。
有3種方法可以重寫
getItemOffsets 名字表示 item 的偏移,實際的工作就是按照需求對 item 的四個方向增加多少 padding
onDraw 表示畫 item 前進行的繪畫
onDrawOver 表示畫 item 後進行的繪畫
所以這三者的順序是 onDraw --》 畫Item內容 --》 onDrawOver
理論上,如果你將要畫的分割線區域不會佔據item的所要顯示的內容(廢話),直接用 drawOver 方法就行。
但是這會帶來『過度繪製』, 不符合我們開發規範。所以一般要用 getItemOffsets 產生偏移。讓item偏移出一些沒有繪製的padding區域。
寫了一個標準的網格佈局分割線
亮點是絕對是偏移和繪製分割線都對的上,『調試GPU過度繪製』檢查妥妥的完美不重疊。 不像網上找來的一大片過度繪製。
public class GridDividerItemDecoration extends RecyclerView.ItemDecoration {
//使用系統自帶的分割線
// private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
private Drawable mDivider;
private int spanCount = -1; //一行有多少列
private int childCount = -1; //有多少個Item
public GridDividerItemDecoration(Context context) {
// final TypedArray a = context.obtainStyledAttributes(ATTRS);
// mDivider = a.getDrawable(0);
// a.recycle();
//使用自己定義的分割線
mDivider = context.getResources().getDrawable(R.drawable.my_divider);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
int visibleItemCount = parent.getChildCount();
for (int i=0; i<visibleItemCount; i++) {
View view = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
//畫豎線
if (!isLastCol(i, getSpanCount(parent))) {
int left = view.getRight() + params.rightMargin;
int right = view.getRight() + mDivider.getIntrinsicWidth() + params.rightMargin;
int top = view.getTop() - params.topMargin;
int bottom = view.getBottom() + params.bottomMargin;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
//畫橫線
if (!isLastRow(i, getSpanCount(parent), getChildCount(parent))) {
int left = view.getLeft() - params.leftMargin;
int right = view.getRight() + mDivider.getIntrinsicWidth() + params.rightMargin;
int top = view.getBottom() + params.bottomMargin;
int bottom = view.getBottom() + mDivider.getIntrinsicHeight() + params.bottomMargin;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
}
/**
* 獲取列數
* @param parent
* @return
*/
private int getSpanCount(RecyclerView parent) {
if (spanCount == -1){
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
}
return spanCount;
}
/**
* 獲取item總數
* @param parent
* @return
*/
private int getChildCount(RecyclerView parent) {
if (childCount == -1) {
childCount = parent.getAdapter().getItemCount();
}
return childCount;
}
/**
* 判斷是否最後一列
* @param pos
* @param spanCount
* @return
*/
private boolean isLastCol(int pos, int spanCount) {
if ((pos + 1) % spanCount == 0) {
return true;
}
return false;
}
/**
* 判斷是否最後一行
* @param pos
* @param spanCount
* @param childCount
* @return
*/
private boolean isLastRow(int pos, int spanCount, int childCount) {
int lastCompletedCount;
if (childCount % spanCount == 0){
lastCompletedCount = (childCount / spanCount - 1) * spanCount;
} else {
lastCompletedCount = childCount - childCount % spanCount;
}
if (pos >= lastCompletedCount)
return true;
return false;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int spanCount = getSpanCount(parent);
int childCount = getChildCount(parent);
int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
int right = mDivider.getIntrinsicWidth();
int bottom = mDivider.getIntrinsicHeight();
//最後一行bottom不用偏移
if (isLastRow(itemPosition, spanCount, childCount)){
bottom = 0;
}
//最後一列right不用偏移
if (isLastCol(itemPosition, spanCount)){
right = 0;
}
outRect.set(0, 0, right, bottom);
}
}
對於分割線顏色和大小:
可以像 DividerItemDecoration 那樣,使用了默認的l分割線。 實際就是顏色是#DCDCDC,大小寬高都是2px的分割線。
也可以自定義,需要寫一個 drawable,比如
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#32f2" />
<size
android:width="1dp"
android:height="1dp" />
</shape>
填對應的了顏色和寬高值。寬代表豎分割線的寬度,高代表橫分割線的寬度。
對於 getItemOffsets 的偏移:
outRect.set(0, 0, right, bottom);是用一個Rect類來存放四個方向的偏移量。
然後會在 LayoutManager 和 RecyclerView 中的測量 measure 和放置 layout 每個 Item 邏輯裏用到(不同LayoutManager用到的地方不一樣).用以擴大整個item的區域.好給之後的draw方法有可繪畫的地方.
如果設置的是Vertical方向滾動。那就是在左右方向的outRect會是Item的向內收縮。上下方向是Item的向外擴張。