想到分割線,原先一直是在item的佈局中直接加入,在adapter中進行判斷,若是最後一個子項則將分割線隱藏,感覺太小兒科了,今天來好好研究這個ItemDecoration的使用。
文章參考自RecyclerView 之 ItemDecoration 講解及高階特性實踐,寫的很詳細,仔細看後就會用了,我只是在此基礎上增添了可以更改顏色、寬度、左右偏移的功能。廢話不多說,我們們開始做吧。
簡單的新增分割線:
一、建立工程,建立Adapter,載入佈局檔案
public class MainActivity extends AppCompatActivity {
@BindView(R.id.recyclerview)
RecyclerView recyclerview;
private List<String> dataList; //資料項
private MyAdapter myAdapter; //介面卡
@Override
protected void onCreate(Bundle savedInstanceState) {
...
initData();
myAdapter = new MyAdapter(R.layout.item_recyclerview,dataList);
recyclerview.setAdapter(myAdapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerview.setLayoutManager(layoutManager);
}
private void initData(){
dataList = new ArrayList<>();
for(int i = 0;i<20;i++){
dataList.add("子項"+i);
}
}
}
複製程式碼
public class MyAdapter extends BaseQuickAdapter<String, BaseViewHolder> {
public MyAdapter(int layoutResId, List<String> data) {
super(layoutResId, data);
}
@Override
protected void convert(BaseViewHolder helper, String item) {
helper.setText(R.id.tv_content, item);
}
}
複製程式碼
二、建立分割器
提前說明,以下內容均是在每個ItemView的頂部加入分割線,第一個不加
通過recyclerview.addItemDecoration(new SimpleItemDecoration());
將以下分割器加入到RecyclerView中即可
public class SimpleItemDecoration extends RecyclerView.ItemDecoration {
/**
* @param outRect 全為0的rect,用來指定偏移區域
* @param view 指RecyclerView中的Item
* @param parent 指RecyclerView本身
* @param state 狀態
*/
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (parent.getChildAdapterPosition(view) != 0) {
//直接設定為1px
outRect.top = 1;
}
}
}
複製程式碼
加入後的效果:
自定義顏色偏移寬度的分割器
以上方法,通過使每個ItemView向上撐出1px距離,而RecyclerView背景為灰色,這樣就顯示出1px的灰色線,實現分割線功能,看到這你可能會想,這也太粗糙了,如果我想要改變分割線的寬度、顏色該怎麼辦,總不能每寫一個RecyclerView都再寫一套分割器,更改RecyclerView背景顏色吧,而且一般分割線並不佔滿全部寬度,有左右偏移,那該怎麼實現呢?
別急,我們先了解下getItemOffsets()方法中的outRect這個引數。
其中的藍色部分為我們的RecyclerView的子項ItemView,外部黃色部分為outRect,只是黃色,並不包含ItemView壓的那部分,left,right,top,bottom四個引數其實就是距離itemView的四個方向的偏移量,是指偏移 ItemView 各個方向的數值,在上面的例子中,我們設定了outRect.top=1
,所以每個ItemView之間有1px的空隙,所以呈現出1px灰色的分割線,分割線顏色決定於RecyclerView的背景色。
一、設定高度:
既然知道了這四個引數代表相對itemview的偏移,那麼分割線的高度就好辦了。
如圖,想要紅色那樣高度的分割線,只需要outRect.top等於該高度就可以了。我們將該高度定義為mDividerHeight
二、設定顏色、左右偏移:
高度有了,如果我們只想繪製紅色那部分矩形而不是ItemView上方的全部該怎麼辦?我們知道每一個View中的onDraw()方法是用來繪製元件的UI效果,所以想要顏色的話,需要我們重寫ItemDecoration中的onDraw()方法。
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state);
複製程式碼
可以看到onDraw()方法中有引數Canvas,通過它來繪製紅色矩形,所以我們需要知道該矩形的四條邊的位置。
float dividerTop = view.getTop() - mDividerHeight; //矩形頂部
float dividerBottom = view.getTop(); //矩形底部
float dividerLeft = parent.getPaddingLeft() + margin; //矩形左側
float dividerRight = parent.getWidth() - parent.getPaddingRight() - margin; //矩形右側
複製程式碼
矩形頂部=itemview的頂部加上分割線的高度,咦?我怎麼寫的減號?看下圖你應該就會明白
安卓中座標是這樣的,向下向右為正,所以紅色矩形頂部位置就應該是itemView的top位置-矩形高度
偏移的話左側加上偏移量,右側減去偏移量即可。
c.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, mPaint);
複製程式碼
這樣我們要繪製的矩形就出來了,等等,我們只是畫了個矩形,還沒顏色呢,再來看看drawRect()中的引數,我們還缺一個mPaint畫筆,通過它來設定矩形分割線顏色。
public MyDecoration() {
mPaint = new Paint();
mPaint.setAntiAlias(true); //抗鋸齒
mPaint.setColor(Color.GRAY); //預設灰色
}
複製程式碼
通過以上步驟,帶有顏色和偏移量,且具有一定高度的分割線就畫好了,其實還沒完,需要注意:getItemOffsets 是針對每一個 ItemView,而 onDraw 方法卻是針對 RecyclerView 本身,所以在 onDraw 方法中需要遍歷螢幕上可見的 ItemView,分別獲取它們的位置資訊,然後分別的繪製對應的分割線。
程式碼如下:
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
int childCount = parent.getChildCount(); //可見ItemView個數
//因為getItemOffsets是針對每一個ItemView,而onDraw方法是針對RecyclerView本身,所以需要迴圈遍歷來設定
for (int i = 0; i < childCount; i++) {
View view = parent.getChildAt(i);
int index = parent.getChildAdapterPosition(view);
//第一個ItemView不需要繪製
if (index == 0) {
continue;//跳過本次迴圈體中尚未執行的語句,立即進行下一次的迴圈條件判斷
}
float dividerTop = view.getTop() - mDividerHeight; //矩形頂部
float dividerLeft = parent.getPaddingLeft() + margin; //矩形左側
float dividerBottom = view.getTop(); //矩形底部
float dividerRight = parent.getWidth() - parent.getPaddingRight() - margin;//矩形右側
c.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, mPaint);
}
}
複製程式碼
在實際運用中,我們的分割線顏色高度等樣式可能不一樣,這裡我們通過建造者模式來設定這些屬性
//設定左右偏移(預設是設定的一樣的,若需要自己更改)
public MyDecoration setMargin(float margin) {
this.margin = margin;
return this;
}
//設定顏色
public MyDecoration setColor(int color) {
mPaint.setColor(color);
return this;
}
//設定分割線高度
public MyDecoration setDividerHeight(float height) {
this.mDividerHeight = height;
return this;
}
複製程式碼
這樣我們就完成了分割線的自定義
完整程式碼如下:
public class MyDecoration extends RecyclerView.ItemDecoration {
private float mDividerHeight = 1; //線的高度
private Paint mPaint; //畫筆將自己做出來的分割線矩形畫出顏色
private float margin = 0; //左右偏移量
public MyDecoration() {
mPaint = new Paint();
mPaint.setAntiAlias(true); //抗鋸齒
mPaint.setColor(Color.GRAY); //預設顏色
}
//通過建造者模式來設定三個屬性
//設定左右偏移(預設是設定的一樣的,若需要自己更改)
public MyDecoration setMargin(float margin) {
this.margin = margin;
return this;
}
//設定顏色
public MyDecoration setColor(int color) {
mPaint.setColor(color);
return this;
}
//設定分割線高度
public MyDecoration setDividerHeight(float height) {
this.mDividerHeight = height;
return this;
}
//在這裡就已經把寬度的偏移給做好了
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//第一個ItemView不需要在上面繪製分割線
if (parent.getChildAdapterPosition(view) != 0) {
outRect.top = (int) mDividerHeight;//指相對itemView頂部的偏移量
}
}
//這裡主要是繪製顏色的
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
int childCount = parent.getChildCount();
//因為getItemOffsets是針對每一個ItemView,而onDraw方法是針對RecyclerView本身,所以需要迴圈遍歷來設定
for (int i = 0; i < childCount; i++) {
View view = parent.getChildAt(i);
int index = parent.getChildAdapterPosition(view);
//第一個ItemView不需要繪製
if (index == 0) {
continue;//跳過本次迴圈體中尚未執行的語句,立即進行下一次的迴圈條件判斷
}
float dividerTop = view.getTop() - mDividerHeight;
float dividerLeft = parent.getPaddingLeft() + margin;
float dividerBottom = view.getTop();
float dividerRight = parent.getWidth() - parent.getPaddingRight() - margin;
c.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, mPaint);
}
}
}
複製程式碼
使用:
MyDecoration myDecoration = new MyDecoration();
myDecoration.setColor(ContextCompat.getColor(getContext(),R.color.line_gray)).setMargin(ConvertUtils.dp2px(getContext(), 15)).setDividerHeight(ConvertUtils.dp2px(getContext(),1));
recyclerView.addItemDecoration(myDecoration);
複製程式碼
實際使用中我們是dp單位,所以這裡我使用了ConvertUtils工具類,將dp轉為px
程式碼如下:
public static int dp2px(Context context, final float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
複製程式碼
看下效果:
到這裡我們想要的功能就全部完成了。(終於碼完了開心!)
如果我們的RecyclerView是橫向的滑動,原理類似,剩下的就交給你們了(懶得寫了嘿嘿)
總結一下
一共兩步:
1、通過getItemOffsets()在itemView頂部撐出來一片區域
2、通過onDraw()方法來在該區域內繪製想要顏色及偏移量的分割線
其實ItemDecoration還有很多很牛逼的地方,例如實現時光軸效果,排行榜的角標,可以看看我參考的那篇文章的實現,寫得很詳細的,是真大佬!日後Demo寫出來了再來更新