preface

RecyclerView for everyone, the basic use of RecyclerView should already be more familiar, here I will directly skip, talk about ItemDecoration, also can be considered to know the new.

  • RecyclerView ItemDecoration (a)
  • RecyclerView Cache (2)
  • 【Android advanced 】RecyclerView drawing process (three)
  • RecyclerView group list add top effect (4)

What is theItemDecoration

From the literal meaning, it is decoration, decoration RecyclerView. How do you understand this? Just a few examples. For example, how should RecyclerView add spacing line? So this is going to be ItemDecoration

 DividerItemDecoration mDivider = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
 rvDemo.addItemDecoration(mDivider);
Copy the code

DividerItemDecoration inherits from ItemDecoration

public class DividerItemDecoration extends ItemDecoration {}
Copy the code

How to write a cool effect?

This involves customizing item decorations

To customize ItemDecoration, three methods are mainly involved

1.getItemOffsets

public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {}
Copy the code

Where, outRect is the spread distance of the item, which defaults to 0. The view as the item; Parent is RecyclerView itself; State is RecyclerView state, which can also be used to transfer parameters between components, which is not involved here temporarily.

For example

<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"
    android:background="#ff0000"
    tools:context=".deco.DecoActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_demo"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</android.support.constraint.ConstraintLayout>
Copy the code

Define a DemoAdapter, BaseRecyclerViewAdapterHelper is used here

public class DemoAdapter extends BaseQuickAdapter<String.BaseViewHolder> {


    public DemoAdapter(@Nullable List<String> data) {
        super(R.layout.item_demo, data);
    }

    @Override
    protected void convert(BaseViewHolder helper, String item) { helper.setText(R.id.tv_demo, item); }}Copy the code

Then assemble the data and add ItemDecoration

         List<String> list = new ArrayList<>();

        for (int i = 0; i < 100; i++) {
            list.add("The first" + i + "Item");
        }

        DemoAdapter demoAdapter = new DemoAdapter(list);
        rvDemo.setLayoutManager(new LinearLayoutManager(this));
        rvDemo.addItemDecoration(new DemoDecoration());
        rvDemo.setAdapter(demoAdapter);
Copy the code

DemoDecoration#getItemOffsets

          @Override
        public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            super.getItemOffsets(outRect, view, parent, state);
            //outRect is the distance between the upper, lower, and left sides of the item. The default value is 0
            outRect.set(10.20.30.40);
      }
Copy the code

As you can see, each item executes getItemOffsets, so we can use this to add spacing lines to RecyclerView. For example, if I set bottom to 1, I won’t do it here, but if you’re interested, you can try it out for yourself.

2.onDraw

 public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {}
Copy the code

Where, C is the canvas of the area supported by getItemOffsets, which can be arbitrarily drawn within the area. If it is outside the area, it will be blocked by Item.

So for example,

        @Override
        public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            super.onDraw(c, parent, state);
            // The canvas corresponding to the blank area of the getItemOffsets can be arbitrarily drawn on the area of the getItemOffsets.

          c.drawCircle(50.50.30, mPaint);
      }

       @Override
        public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            super.getItemOffsets(outRect, view, parent, state);
            //outRect is the distance between the upper, lower, and left sides of the item. The default value is 0
          outRect.set(100.0.0.5);
}
Copy the code

The renderings are as follows

As you can see, the onDraw method is executed only once. What if I wanted to draw a circle before each item?

         @Override
        public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
             int itemCount = parent.getChildCount();

            for (int i = 0; i < itemCount; i++) {
                    View child = parent.getChildAt(i);
                    int cx = child.getWidth() / 2;
                    int cy = child.getTop() + child.getHeight() / 2;
                    c.drawCircle(cx, cy, 30, mPaint); }}}Copy the code

The renderings are as follows

Combining these two methods, can we make a group effect? Look at the renderings first


 public DemoDecoration(a) {
            mPaint = new Paint();
            mPaint.setColor(Color.GREEN);
            mPaint.setTextSize(DensityUtils.dp2px(DecoActivity.this.16));
        }
  @Override
        public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            super.getItemOffsets(outRect, view, parent, state);
            //outRect is the distance between the upper, lower, and left sides of the item. The default value is 0

            int index = parent.getChildAdapterPosition(view);
            if (index % 3= =0) {
                outRect.set(0.100.0.5);
            } else {
                outRect.set(0.0.0.5); }}@Override
        public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            super.onDraw(c, parent, state);
            // The canvas corresponding to the blank area of the getItemOffsets can be arbitrarily drawn on the area of the getItemOffsets.
            int itemCount = parent.getChildCount();
            for (int i = 0; i < itemCount; i++) {
                View child = parent.getChildAt(i);
                int cx = child.getWidth() / 2 - 50;
                int cy = child.getTop() - 50 + DensityUtils.dp2px(DecoActivity.this.16) / 2;
                int index = parent.getChildAdapterPosition(child);
                if (index % 3= =0) {
                    String text = "The first" + index / 3 + "Group";
                    c.drawText(text, 0, text.length(), cx, cy, mPaint); }}}Copy the code

Of course, the group of the title position calculation is not prepared, here is just to show the effect. For ideal effect, you can refer to the “Android View Effect” group list to achieve the top suction effect

3.onDrawOver

public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {}
Copy the code

OnDrawOver, like onDraw, is only done once, and it’s drawn last, so it can be drawn on the item.

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

            int itemCount = parent.getChildCount();
            for (int i = 0; i < itemCount; i++) {
                View child = parent.getChildAt(i);
                int cx = child.getWidth() / 2;
                int cy = child.getTop() + child.getHeight()/2 ;
                c.drawCircle(cx, cy, 30, mPaint); }}Copy the code

The renderings are as follows

What does this approach do? Look at the renderings first

You can see that there is a fade effect at the top of the list. The complete code is as follows

public class TransDecoration extends RecyclerView.ItemDecoration {
    private Paint mPaint;
    private Xfermode xfermode;
    private LinearGradient linearGradient;
    private int layerId;

    public TransDecoration(a) {
        mPaint = new Paint();
        xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
        linearGradient = new LinearGradient(0.0 f.0.0 f.0.0 f.100.0 f.new int[] {0, Color.BLACK}, null, Shader.TileMode.CLAMP);
    }

    @Override
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDraw(c, parent, state);
        // The Paint parameter is passed null, and the first black screen will blink when mPaint is passed
        // Note that saveLayer cannot be saved or moved into the onDrawOver method
        layerId = c.saveLayer(0.0 f.0.0 f, (float) parent.getWidth(), (float) parent.getHeight(), null, Canvas.ALL_SAVE_FLAG);
    }

    @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 onDrawOver(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDrawOver(canvas, parent, state);
        mPaint.setXfermode(xfermode);
        mPaint.setShader(linearGradient);
        canvas.drawRect(0.0 f.0.0 f, parent.getRight(), 200.0 f, mPaint);
        mPaint.setXfermode(null); canvas.restoreToCount(layerId); }}Copy the code

PorterDuffXfermode is the blending Mode of the image. For details, please refer to the separate breaking to figure out porterDuff. Mode LinearGradient is the gradual effect. For details, please refer to the drawing part of the Custom controls trilogy (19) – LinearGradient and flashing text effects

Complete source codePicRvDemo

Your recognition is the motivation for me to keep updating my blog. If it is useful, please give me a thumbs-up. Thank you