RecyclerView ItemDecoration 完全解析

我們都知道,使用 RecyclerView 時 ,我們不能像 ListView 那樣通過 setDivider() 的方式來設置分割線,好在 Android 爲我們提供了定製性更強的 ItemDecoration 來爲 RecyclerView 設置分割線。

什麼是 ItemDecoration ?

顧名思義 ItemDecoration 就是 Item 的裝飾,我們可以在 Item 的上下左右添加自定義的裝飾,比如 橫線,圖案。同時系統已經爲我們提供了一個 DividerItemDecoration, 如果這個 DividerItemDecoration 不滿足我們的需求,我們就可以通過自定義 ItemDecoration 來實現了。

下面我們看下系統的 DividerItemDecoration :

DividerItemDecoration(系統提供)

DividerItemDecoration 的使用非常簡單,只需添加下面代碼即可:

DividerItemDecoration decoration = new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);
recyclerView.addItemDecoration(decoration);

效果:

如果想要修改 DividerItemDecoration 的顏色和高度,可以調用它的 setDrawable(drawable) 設置一個 Drawable 對象

// MainActivity.java
DividerItemDecoration decoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
Drawable dividerDrawable = getResources().getDrawable(R.drawable.drawable_divider);
decoration.setDrawable(dividerDrawable);
recyclerView.addItemDecoration(decoration);

// res/drawable/drawable_divider.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    android:shape="rectangle">
    <!-- 漸變色 -->
    <gradient
        android:angle="135"
        android:centerColor="#4CAF50"
        android:endColor="#2E7D32"
        android:startColor="#81C784"
        android:type="linear" />

    <size android:height="1dp" />
</shape>

效果:

自定義 ItemDecoration

如果上述 DividerItemDecoration 不能滿足我們的需求,那麼我們就可以自定義 ItemDecoration ,ItemDecoration 的自定義主要需要重寫以下三個方法:

/**
 * 自定義 ItemDecoration
 */
public class LinearItemDecoration extends RecyclerView.ItemDecoration {

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
    }

    @Override
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDraw(c, parent, state);
    }

    @Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
    }

1. getItemOffsets()

getItemOffsets() 主要作用是在 item 的四周留下邊距,效果和 margin 類似,item 的四周留下邊距後,我們就可以通過 onDraw() 在這個邊距上繪製了。

public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) 

(1) 參數 Rect outRect : 表示 item 的上下左右所留下的邊距。其中 outRect 的 left, top,right,bottom 即爲 item 四周留下的邊距的距離,默認都爲 0 ;示意圖如下:

getOffsets

(2) 參數 View view : 指當前 item 的 View 對象;

(3) 參數 RecyclerView parent : 指 RecyclerView 本身;

(4) RecyclerView.State state : 指 RecyclerView 當前的狀態;

1.1 getItemOffsets() 應用例子:

既然 getItemOffsets(Rect outRect) 方法可以設置 item 四周的邊距大小,那就可以設置 recyclerview 背景色和 item 四周的邊距,使得 item 四周的邊距透出 recyclerview 背景色來達到分割線的目的。

當然 item 的背景色需要和 recyclerview 的背景色不一致纔有效果;

首先將 recyclerview 的背景色設置爲 colorAccent 紅色, 將 item 的背景色設置爲 白色:

<!--- activity_main.xml -----> 
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorAccent" />

</android.support.constraint.ConstraintLayout>
<!--- item_recycler.xml -----> 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="#fff"
    android:gravity="center_vertical"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:gravity="center_vertical" />

</LinearLayout>

然後繼承 ItemDecoration 類,重寫 getOffsets() 方法,將 outRect 的上邊距 top 設置爲 10px;

/**
 * 自定義 ItemDecoration
 */
public class LinearItemDecoration extends RecyclerView.ItemDecoration {

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        outRect.top = 10;   // 10px
    }
}

最後將這個 LinearItemDecoration 添加到 RecyclerView 中:

LinearItemDecoration decoration = new LinearItemDecoration();
recyclerView.addItemDecoration(decoration);

效果如下(下方的紅色是 因爲RecyclerView 高度爲 match_parent ,但 item 數據只有 5 條):

從效果圖可以看出:每一個 item 的 頂部都有一個紅色的背景線,包括第一個 item 頂部也有(怎麼解決呢?見 2.1 節詳解)

同理,我們可以設置爲 底部 10px,左側 20px ,右側 40px;

public class LinearItemDecoration extends RecyclerView.ItemDecoration {

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        outRect.bottom = 10;
        outRect.left = 20;
        outRect.right = 40;
    }
}

效果:

可以看到:每個 item 的左測,底部,右側都有了間距,露出了 RecyclerView 的背景色了。

2. onDraw()

@Override
public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
   super.onDraw(canvas, parent, state);
}

onDraw() 函數中的 parent , state 參數和 getItemOffsets() 方法中的參數含義是一樣的,canvas 參數是 getItemOffsets() 函數所留下的左右上下的空白區域對應的 Canvas 畫布對象。我們可以在這個區域中利用 Paint 畫筆繪製任何圖形。

比如在 item 左側繪製一個 空心圓。

/**
 * 自定義 ItemDecoration
 */
public class LinearItemDecoration extends RecyclerView.ItemDecoration {

    private static final String TAG = "LinearItemDecoration";
    private Paint paint;

    public LinearItemDecoration() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);
    }

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        Log.e(TAG, "getItemOffsets: " );
        outRect.bottom = 10;
        outRect.left = 100;
        outRect.right = 40;
    }

    @Override
    public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDraw(canvas, parent, state);
        Log.e(TAG, "onDraw: ");
        for (int i = 0; i < parent.getChildCount(); i++) {
            View childView = parent.getChildAt(i);
            canvas.drawCircle(50, childView.getTop() + childView.getHeight() / 2, 20, paint);
        }

    }
}

效果:

我們在 getItemOffsets() 和 onDraw() 方法中都添加了日誌,日誌打印如下:

log

從日誌中可以看出: getItemOffsets() 方法執行了 5 遍,和 數據源個數是一樣的,但 onDraw() 方法只執行了一遍,由此我們知道, getItemOffsets() 是針對每個 item 都會執行一次,也就是說 每個 item 的 outRect 可以設置爲不同值,但是 onDraw(),onDrawOver() 是針對 ItemDecoration 的,不是針對 item 的,只執行一次。所以我們在 onDraw() ,onDrawOver() 中繪製的時候,需要遍歷每個 item 進行繪製。

優雅獲取 outRect 中的值

在上面例子中,我們在 onDraw 中獲取 outRect 中的值都是寫成計算好的固定值,顯然這種硬編碼的方式不利於擴展,其實,我們可以通過 LayoutManager 來獲取 getItemOffsets() 中設置的 outRect 的值。

@Override
public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    super.onDraw(canvas, parent, state);
    Log.e(TAG, "onDraw: ");
    RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
    for (int i = 0; i < parent.getChildCount(); i++) {
        View childView = parent.getChildAt(i);
         int leftDecorationWidth = layoutManager.getLeftDecorationWidth(childView);
         int topDecorationHeight = layoutManager.getTopDecorationHeight(childView);
         int rightDecorationWidth = layoutManager.getRightDecorationWidth(childView);
         int bottomDecorationHeight = layoutManager.getBottomDecorationHeight(childView);
    }
}

上面硬編碼可以改成:

@Override
public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    super.onDraw(canvas, parent, state);
    Log.e(TAG, "onDraw: ");
    RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
    for (int i = 0; i < parent.getChildCount(); i++) {
        View childView = parent.getChildAt(i);
        int leftDecorationWidth = layoutManager.getLeftDecorationWidth(childView);
        int left = leftDecorationWidth / 2;
        canvas.drawCircle(left, childView.getTop() + childView.getHeight() / 2, 20, paint);
    }
}
2.1 擴展1 – 減少背景設置,避免過度繪製

爲了減少過度繪製,我們將 activity_main 中 RecyclerView , item_recycler 中的背景全部去掉,不設置任何背景,然後在 LinearItemDecoration 中進行純繪製分割線,代碼如下:

public class LinearItemDecoration extends RecyclerView.ItemDecoration {

    private static final String TAG = "LinearItemDecoration";
    private Paint paint;
    private Paint dividerPaint;

    public LinearItemDecoration() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);

        dividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        dividerPaint.setColor(Color.parseColor("#e6e6e6"));
        dividerPaint.setStyle(Paint.Style.FILL);
    }

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        Log.e("ItemOffsets", "getItemOffsets: ");
        outRect.bottom = 5;
        outRect.left = 100;
    }

    @Override
    public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDraw(canvas, parent, state);
        Log.e(TAG, "onDraw: ");
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        for (int i = 0; i < parent.getChildCount(); i++) {
            View childView = parent.getChildAt(i);
            int leftDecorationWidth = layoutManager.getLeftDecorationWidth(childView);
            int topDecorationHeight = layoutManager.getTopDecorationHeight(childView);
            int rightDecorationWidth = layoutManager.getRightDecorationWidth(childView);
            int bottomDecorationHeight = layoutManager.getBottomDecorationHeight(childView);
            int left = leftDecorationWidth / 2;
            canvas.drawCircle(left, childView.getTop() + childView.getHeight() / 2, 20, paint);
            // getItemOffsets()中的設置的是 bottom = 5px;所以在 drawRect 時,top 爲 childView.getBottom,bottom爲top+bottomDecorationHeight
            canvas.drawRect(new Rect(
                    leftDecorationWidth,
                    childView.getBottom(),
                    childView.getWidth() + leftDecorationWidth,
                    childView.getBottom() + bottomDecorationHeight
            ), dividerPaint);
        }
    }

    @Override
    public void onDrawOver(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDrawOver(canvas, parent, state);
    }
}

效果:

注意: 上面 getItemOffsets() 中設置的是 bottom = 5px; 所以在 onDraw() 方法的 drawRect 時,top值爲 childView.getBottom, bottom值爲 top+bottomDecorationHeight。

divider-top-bottom

同理:getItemOffsets() 中設置是 top = 5px, 那麼在 onDraw() 方法 drawRect 時,bottom 值爲 childView.getTop() , top 值爲 bottom - topDecorationHeight

2.2 擴展2 – 實現豎直進度分割線

在 getItemOffsets() 方法中左側留下空白區域,然後在 onDraw() 方法中繪製 圓和 豎線。代碼如下:

<!--- item_recycler.xml ---->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    android:gravity="center_vertical"
    android:orientation="vertical"
    android:padding="10dp">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#332"
        android:textSize="16sp"
        tools:text="我是 title" />

    <TextView
        android:id="@+id/tv_time"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:gravity="center_vertical"
        android:textColor="#666"
        android:textSize="14sp"
        tools:text="woshi" />

</LinearLayout>

<!--- activity_main.xml --->
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#dfdfdf" />

</android.support.constraint.ConstraintLayout>
// ProgressItemDecoration.java
public class ProgressItemDecoration extends RecyclerView.ItemDecoration {

    private Context context;
    private Paint circlePaint;
    private Paint linePaint;
    private int radius;
    private int curPosition = 0;   // 當前進行中的位置

    public ProgressItemDecoration(Context context) {
        this.context = context;
        circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        circlePaint.setColor(context.getResources().getColor(R.color.colorPrimary));
        circlePaint.setStyle(Paint.Style.FILL);
        radius = dp2Px(8);
        circlePaint.setStrokeWidth(dp2Px(2));


        linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        linePaint.setColor(context.getResources().getColor(R.color.colorPrimary));
        linePaint.setStrokeWidth(dp2Px(2));
    }

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        outRect.top = dp2Px(20);
        outRect.left = dp2Px(50);
        outRect.right = dp2Px(20);

    }

    @Override
    public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDraw(canvas, parent, state);
        int childCount = parent.getChildCount();
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        for (int i = 0; i < childCount; i++) {
            View childView = parent.getChildAt(i);
            int leftDecorationWidth = layoutManager.getLeftDecorationWidth(childView);
            int topDecorationHeight = layoutManager.getTopDecorationHeight(childView);
            // 獲取當前 item 是 recyclerview 的第幾個 childview
            int childLayoutPosition = parent.getChildLayoutPosition(childView);
            float startX = leftDecorationWidth / 2;
            float stopX = startX;
            // 圓頂部部分豎線,起點 Y
            float topStartY = childView.getTop() - topDecorationHeight;
            // 圓頂部部分豎線,終點 Y
            float topStopY = childView.getTop() + childView.getHeight() / 2 - radius;

            // 圓底部部分豎線,起點 Y
            float bottomStartY = childView.getTop() + childView.getHeight() / 2 + radius;
            // 圓底部部分豎線,終點 Y
            float bottomStopY = childView.getBottom();

            // 位置超過 curPosition 時,豎線顏色設置爲淺色
            if (childLayoutPosition > curPosition) {
                linePaint.setColor(context.getResources().getColor(R.color.colorPrimaryTint));
                circlePaint.setColor(context.getResources().getColor(R.color.colorPrimaryTint));
                circlePaint.setStyle(Paint.Style.STROKE);
            } else {
                linePaint.setColor(context.getResources().getColor(R.color.colorPrimary));
                circlePaint.setColor(context.getResources().getColor(R.color.colorPrimary));
                circlePaint.setStyle(Paint.Style.FILL);
            }

            // 繪製圓
            if (childLayoutPosition == curPosition) {
                circlePaint.setStyle(Paint.Style.STROKE);
                canvas.drawCircle(leftDecorationWidth / 2, childView.getTop() + childView.getHeight() / 2, dp2Px(2), circlePaint);
            }
            canvas.drawCircle(leftDecorationWidth / 2, childView.getTop() + childView.getHeight() / 2, radius, circlePaint);

            // 繪製豎線 , 第 0 位置上只需繪製 下半部分
            if (childLayoutPosition == 0) {
                // 當前 item position = curPosition 時,繪製下半部分豎線時,顏色設置爲淺色
                if (childLayoutPosition == curPosition) {
                    linePaint.setColor(context.getResources().getColor(R.color.colorPrimaryTint));
                }
                canvas.drawLine(startX, bottomStartY, startX, bottomStopY, linePaint);
                // 最後位置上,只需繪製上半部分
            } else if (childLayoutPosition == parent.getAdapter().getItemCount() - 1) {
                canvas.drawLine(startX, topStartY, startX, topStopY, linePaint);
            } else {
                // 都要繪製
                canvas.drawLine(startX, topStartY, startX, topStopY, linePaint);
                // 當前 item position = curPosition 時,繪製下半部分豎線時,顏色設置爲淺色
                if (childLayoutPosition == curPosition) {
                    linePaint.setColor(context.getResources().getColor(R.color.colorPrimaryTint));
                }
                canvas.drawLine(startX, bottomStartY, startX, bottomStopY, linePaint);
            }
        }

    }

    /**
     * 設置進行中的位置
     *
     * @param recyclerView
     * @param position
     */
    public void setDoingPosition(RecyclerView recyclerView, int position) {
        if (recyclerView == null) {
            throw new IllegalArgumentException("RecyclerView can't be null");
        }
        if (recyclerView.getAdapter() == null) {
            throw new IllegalArgumentException("RecyclerView Adapter can't be null");
        }
        if (position < 0) {
            throw new IllegalArgumentException("position can't be less than 0");
        }
        recyclerView.getLayoutManager().getItemCount();
        if (position > recyclerView.getAdapter().getItemCount() - 1) {
            throw new IllegalArgumentException("position can't be greater than item count");
        }
        this.curPosition = position;
    }

    private int dp2Px(int value) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, context.getResources().getDisplayMetrics());
    }
}

// MainActivity.java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        RecyclerView recyclerView = findViewById(R.id.rv_recycler);
        recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            String data = "test data  - " + i;
            if (i % 2 == 0) {
                data = data + "\n" + "這是測試數據 " + i;
            }
            list.add(data);
        }
        BaseRecyclerAdapter adapter = new BaseRecyclerAdapter<String>(list, R.layout.item_recycler) {

            @Override
            protected void bind(BaseRecyclerAdapter<String> adapter, BaseViewHolder holder, String data, int position) {
                holder.setText(R.id.tv_title, data);
                holder.setText(R.id.tv_time, new Date().toString());
            }
        };
        recyclerView.setAdapter(adapter);
        ProgressItemDecoration decoration = new ProgressItemDecoration(this);
        decoration.setDoingPosition(recyclerView, 0);
        recyclerView.addItemDecoration(decoration);
    }
}

效果:

3. onDrawOver()

當我們將上面例子中繪製左側的空心圓的位置改爲: 圓心 x 座標爲 leftDecorationWidth,同時將 item 背景色設置爲 白色:

 @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        Log.e("ItemOffsets", "getItemOffsets: ");
        outRect.bottom = 5;
        outRect.left = 100;
    }

    @Override
    public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDraw(canvas, parent, state);
        Log.e(TAG, "onDraw: ");
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        for (int i = 0; i < parent.getChildCount(); i++) {
            View childView = parent.getChildAt(i);
            int leftDecorationWidth = layoutManager.getLeftDecorationWidth(childView);
            int bottomDecorationHeight = layoutManager.getBottomDecorationHeight(childView);
            int left = leftDecorationWidth / 2;
//            canvas.drawCircle(left, childView.getTop() + childView.getHeight() / 2, 20, paint);
            canvas.drawCircle(leftDecorationWidth, childView.getTop() + childView.getHeight() / 2, 20, paint);
            // getItemOffsets()中的設置的是 bottom = 5px;所以在 drawRect 時,top 爲 childView.getBottom,bottom爲top+bottomDecorationHeight
            canvas.drawRect(new Rect(
                    leftDecorationWidth,
                    childView.getBottom(),
                    childView.getWidth() + leftDecorationWidth,
                    childView.getBottom() + bottomDecorationHeight
            ), dividerPaint);
        }
    }

效果:

我們發現:繪製的空心圓被 item 遮擋了右邊部分,變爲不可見了,這是因爲在這個繪製的流程中,先調用 ItemDecoration 的 onDraw() 方法,然後再調用 item 的 onDraw() 方法,最後再調用 ItemDecoration 的 onDrawOver() 方法。

因此,當我們想要在 item 的繪製顯示一些內容時,將繪製的邏輯寫在 onDrawOver() 方法即可。

下面我們實現在 item 與 左側 decoration 交匯處繪製一個 apple icon ,並將 item 中的內容文本設置爲居中顯示,代碼如下:

public class LinearItemDecoration extends RecyclerView.ItemDecoration {

    private static final String TAG = "LinearItemDecoration";
    private Paint paint;
    private Paint dividerPaint;
    private Bitmap iconBitmap;

    public LinearItemDecoration(Context context) {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);

        dividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        dividerPaint.setColor(Color.parseColor("#e6e6e6"));
        dividerPaint.setStyle(Paint.Style.FILL);

        iconBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.ios);
    }

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        Log.e("ItemOffsets", "getItemOffsets: ");
        outRect.bottom = 5;
        outRect.left = 100;
    }

    @Override
    public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDraw(canvas, parent, state);
        Log.e(TAG, "onDraw: ");
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        for (int i = 0; i < parent.getChildCount(); i++) {
            View childView = parent.getChildAt(i);
            int leftDecorationWidth = layoutManager.getLeftDecorationWidth(childView)int bottomDecorationHeight = layoutManager.getBottomDecorationHeight(childView);
            int left = leftDecorationWidth / 2;
//            canvas.drawCircle(left, childView.getTop() + childView.getHeight() / 2, 20, paint);
//            canvas.drawCircle(leftDecorationWidth, childView.getTop() + childView.getHeight() / 2, 20, paint);

            // getItemOffsets()中的設置的是 bottom = 5px;所以在 drawRect 時,top 爲 childView.getBottom,bottom爲top+bottomDecorationHeight
            canvas.drawRect(new Rect(
                    leftDecorationWidth,
                    childView.getBottom(),
                    childView.getWidth() + leftDecorationWidth,
                    childView.getBottom() + bottomDecorationHeight
            ), dividerPaint);
        }
    }

    @Override
    public void onDrawOver(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDrawOver(canvas, parent, state);
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        for (int i = 0; i < parent.getChildCount(); i++) {
            View childView = parent.getChildAt(i);
            int leftDecorationWidth = layoutManager.getLeftDecorationWidth(childView);
            canvas.drawBitmap(iconBitmap, leftDecorationWidth - iconBitmap.getWidth() / 2,
                    childView.getTop() + childView.getHeight() / 2 - iconBitmap.getHeight() / 2, paint);
        }
    }
}

效果:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章