1. 程式人生 > >RecyclerView自定義分割線實戰

RecyclerView自定義分割線實戰

前言

RecyclerView已經推出很久了,由於其高度的可定製性現在被廣泛應用,我們常用的功能,如:單條目更新,LayoutManager實現各種炫酷的排列效果,定義個性分割線等

今天學習如何定製一個自己的分割線,讓你的列表看起來更好看

內容部分

首先:常規的用法三步走設定佈局方式,設定分割線,設定adapter。

本身系統是自帶了一個預設的分割線類DividerItemDecoration可以實現和ListView一樣的效果。但是我們可能有其他的需求,如我們希望分割線有不同的顏色,這時我們可以通過DividerItemDecoration的setDrawable(Drawable drawable)方法設定一個Drawable進入,如下:

 DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
        Drawable drawable = getResources().getDrawable(R.drawable.mycoler);
        dividerItemDecoration.setDrawable(drawable);
        recyclerView.addItemDecoration(dividerItemDecoration)
;

通過編寫不同的shape可以設定成不同的顏色的線。如圖:
在這裡插入圖片描述

特殊情況問題記錄

當我在繪製橫向滾動的RecyclerView的分割線的時候,出了一個小問題,因為使用shape畫的線,獲取Drawable的寬度一直為1px導致,無法顯示分割線,如果你傳入的是一個圖片就沒問題,我還沒找到原因。

描述:RecyclerView高度為100dp,item的高度為50dp,當繪製出豎直方向分割線的時候,item部分分割線未能顯示,經過排查發現,分割線是繪製在RecyclerView的view上,item作為子view存在於RecyclerView的容器中,所以導致分割線繪製的一部分被遮擋,此處我使用系統提供的DividerItemDecoration也是效果一致的。如下圖:

在這裡插入圖片描述

所繪製的分割線上繪製在RecyclerView的佈局上的,這也就解釋了,為什麼繪製完分割線需要呼叫getItemOffsets()方法進行位置重新排列了

如何解決這個情況呢?(其實就是自己根據出傳入的Drawable寬度處理)

1.給item設定一個padding值就可以了,這樣就可以把分割線顯示出來了。

2.通過getItemOffsets()方法設定偏移量,讓item順位後移,顯示出來分割線即可。

處理後的圖片如下:

在這裡插入圖片描述

不過這個問題我會在嘗試,找出為什麼拿到的寬度數值不正確,如有知道的大佬,望告知,謝謝

定製過程分析

步驟一

初始化一些引數內容,如偏移量,預設的分割線等

   public MyItemDecoration(Context context, int orientation, int inset, Drawable drawable) {
        if (orientation != VERTICAL && orientation != HORIZONTAL) {
            throw new IllegalArgumentException("請輸入正確的引數!");
        }
        this.inset = inset;
        mOrientation = orientation;
        mDivider = drawable;
        if (drawable == null) {
            final TypedArray a = context.obtainStyledAttributes(ATTRS);
            mDivider = a.getDrawable(0);
            a.recycle();
        }
    }

步驟二

繪製部分,通過標識位來區分是垂直方向還是水平方向

@Override
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        if (parent.getLayoutManager() != null && this.mDivider != null) {
            if (this.mOrientation == VERTICAL) {
                this.drawVertical(c, parent);
            } else {
                this.drawHorizontal(c, parent);
            }

        }
    }

步驟三

繪製垂直或水平的分割線,這裡主要是設定Drawable在RecyclerView中的位置。

private void drawHorizontal(Canvas canvas, RecyclerView parent) {
        int top = parent.getPaddingTop();
        int bottom = parent.getHeight() - parent.getPaddingBottom();

        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount - 1; i++) {
            View childAt = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childAt.getLayoutParams();
            int left = childAt.getRight() + layoutParams.rightMargin;
            int right = mDivider.getIntrinsicWidth() + left;

            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(canvas);

        }


    }

    private void drawVertical(Canvas canvas, RecyclerView parent) {
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount - 1; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
            int top = child.getBottom() + layoutParams.bottomMargin;
            int bottom = top + mDivider.getIntrinsicHeight();

            if (inset > 0) {
                mDivider.setBounds(left + inset, top, right - inset, bottom);
            } else {
                mDivider.setBounds(left, top, right, bottom);
            }
            mDivider.draw(canvas);
        }
    }

步驟四

因為我們為RecyclerView添加了分割線,所以整體位置要做調整。主要調整的部分就是,在條目中間新增一個分割線,需要對原來的所有條目後移動一個分割線的寬度(高度)。

 @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        if (this.mDivider == null) {
            outRect.set(0, 0, 0, 0);
        } else {
            if (this.mOrientation == VERTICAL) {
                outRect.set(0, 0, 0, this.mDivider.getIntrinsicHeight());
            } else {
                outRect.set(0, 0, this.mDivider.getIntrinsicWidth(), 0);
            }

        }
    }

以上就是基本的步驟的,其實就是參考系統提供的DividerItemDecoration來寫就行。其實系統的程式碼,是最好的例項,所以多讀原始碼對能力的提升很有幫助。

另外一種實現分割線的方式

其實這種方式很簡單,就是將分割線放到item佈局中。感覺也是不錯的方案,畢竟在做條目佈局的時候就把這個分割線完成了。

finish以上完畢,有問題謝謝指出。