When I think of the dividing line, I used to add the dividing line directly into the layout of the item and judge it in the Adapter. If the last item is a child, I will hide the dividing line. Today, I will study the use of this item decoration.

Article reference since RecyclerView ItemDecoration explained and advanced characteristics of practice, write very detailed, look carefully will use, I just added on this basis can change the color, width, left and right offset function. Without further ado, let’s get to work.

Simply add a dividing line:

First, establish the project, create Adapter, load layout file

public class MainActivity extends AppCompatActivity {
    @BindView(R.id.recyclerview)
    RecyclerView recyclerview;			
    private List<String> dataList;		/ / data items
    private MyAdapter myAdapter;		/ / adapter

    @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(a){
        dataList = new ArrayList<>();
        for(int i = 0; i<20; i++){ dataList.add("Child"+i); }}}Copy the code
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); }}Copy the code

Two, the establishment of a divider

To be clear, the following items are added to the top of each ItemView. The first item is not added

Through recyclerview. AddItemDecoration (new SimpleItemDecoration ()); Add the following divider to RecyclerView

public class SimpleItemDecoration extends RecyclerView.ItemDecoration {
    / * * *@paramOutRect Rect with all zeros, used to specify the offset region *@paramView refers to the RecyclerView Item *@paramParent refers to RecyclerView itself@paramState of the 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) {
            // Set it to 1px
            outRect.top = 1; }}}Copy the code

Effect after addition:

Custom color offset width divider

In the above method, make each ItemView stretch up 1px distance, and RecyclerView background is gray, so 1px gray line is displayed, to achieve the separation line function, you may think, this is too rough, if I want to change the width and color of the separation line, what should I do? Always can’t write every RecyclerView to write a set of separators, change RecyclerView background color, and the general division line does not account for the full width, there is left and right offset, how to achieve it?

Let’s look at the outRect parameter in the getItemOffsets() method.

The blue part is a subitem of Our RecyclerView ItemView, and the outer yellow part is outRect, which is only yellow and does not contain the part of ItemView pressure. The left,right,top, and bottom parameters are the offset from the four directions of the itemView. They are the offset from each direction of the itemView. In the above example, we set outrect. top=1, so there is 1px space between each itemView. Therefore, a 1px gray division line is presented, and the color of the division line depends on the background color of RecyclerView.

First, set the height:

Now that we know that these four parameters represent the offset relative to the ItemView, we can figure out the height of the dividing line.

In the figure, if you want a splitter line of the red height, all you need is for outrect. top to be equal to that height. We define this height as mDividerHeight

Two, set the color, left and right offset:

Now that we have the height, what if we only want to draw the rectangle in red instead of the entire rectangle above the ItemView? We know that the onDraw() method in each View is used to draw the UI effect of the component, so we need to rewrite the onDraw() method in ItemDecoration to get a color.

public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state);
Copy the code

You can see that the onDraw() method has the Canvas parameter to draw the red rectangle, so we need to know the positions of the four edges of the rectangle.

float dividerTop = view.getTop() - mDividerHeight;  						// Top of rectangle
float dividerBottom = view.getTop();									   // Bottom of rectangle
float dividerLeft = parent.getPaddingLeft() + margin;                         // Left side of rectangle
float dividerRight = parent.getWidth() - parent.getPaddingRight() - margin;   // The right side of the rectangle
Copy the code

Top of rectangle = top of ItemView plus the height of the dividing line, huh? How did I write the minus sign? Look at the picture below and you’ll see

In Android, the coordinates look like this, downward to the right is positive, so the top of the red rectangle should be the top position of the itemView — the height of the rectangle

For offset, add offset to the left and subtract offset from the right.

c.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, mPaint);
Copy the code

So there we have the rectangle we want to draw, wait, we just drew the rectangle, we don’t have the color yet, let’s look at the parameters in drawRect(), we still need a mPaint brush to set the color of the rectangle separator.

public MyDecoration(a) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);          / / anti-aliasing
        mPaint.setColor(Color.GRAY);        // Grey by default
}
Copy the code

With the above steps, the dividing line with color and offset and a certain height is drawn. This is not the end of the story. Note: The getItemOffsets are for each ItemView, while the onDraw method is for RecyclerView itself, so the onDraw method needs to traverse the visible ItemView on the screen to obtain their location information respectively. Then draw the corresponding dividing line respectively.

The code is as follows:

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

        int childCount = parent.getChildCount();	// See the number of ItemViews
// Because the getItemOffsets are used for each ItemView, the onDraw method is used for RecyclerView itself, so it needs to loop through the Settings
        for (int i = 0; i < childCount; i++) {
            View view = parent.getChildAt(i);
            int index = parent.getChildAdapterPosition(view);
            // The first ItemView does not need to be drawn
            if (index == 0) {
                continue;// Skip statements that have not yet been executed in this loop body, and proceed to the next loop condition immediately
            }
          float dividerTop = view.getTop() - mDividerHeight;                	// Top of rectangle
          float dividerLeft = parent.getPaddingLeft() + margin;            		// Left side of rectangle
          float dividerBottom = view.getTop();									// Bottom of rectangle
          float dividerRight = parent.getWidth() - parent.getPaddingRight() - margin;// The right side of the rectanglec.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, mPaint); }}Copy the code

In practice, our splitter, color, height, etc., may be different. Here we use Builder mode to set these properties

// Set left/right offset (default is the same, if you need to change)
    public MyDecoration setMargin(float margin) {
        this.margin = margin;
        return this;
    }
    // Set the color
    public MyDecoration setColor(int color) {
        mPaint.setColor(color);
        return this;
    }
    // Set the dividing line height
    public MyDecoration setDividerHeight(float height) {
        this.mDividerHeight = height;
        return this;
    }
Copy the code

So we’ve finished customizing the divider

The complete code is as follows:

public class MyDecoration extends RecyclerView.ItemDecoration {

    private float mDividerHeight = 1; // Line height
    private Paint mPaint;           // The brush will draw the color of the divider rectangle
    private float margin = 0;       // Left/right offset

    public MyDecoration(a) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);          / / anti-aliasing
        mPaint.setColor(Color.GRAY);        // Default color
    }

    // Set three properties in builder mode
    // Set left/right offset (default is the same, if you need to change)
    public MyDecoration setMargin(float margin) {
        this.margin = margin;
        return this;
    }

    // Set the color
    public MyDecoration setColor(int color) {
        mPaint.setColor(color);
        return this;
    }

    // Set the dividing line height
    public MyDecoration setDividerHeight(float height) {
        this.mDividerHeight = height;
        return this;
    }

    // The width offset is already done here
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        // The first ItemView does not need a dividing line drawn on it
        if(parent.getChildAdapterPosition(view) ! =0) {
            
            outRect.top = (int) mDividerHeight;// Refers to the offset relative to the top of itemView}}// Here is mainly to draw color
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);

        int childCount = parent.getChildCount();
// Because the getItemOffsets are used for each ItemView, the onDraw method is used for RecyclerView itself, so it needs to loop through the Settings
        for (int i = 0; i < childCount; i++) {
            View view = parent.getChildAt(i);
            int index = parent.getChildAdapterPosition(view);
            // The first ItemView does not need to be drawn
            if (index == 0) {
                continue;// Skip statements that have not yet been executed in this loop body, and proceed to the next loop condition immediately
            }
            float dividerTop = view.getTop() - mDividerHeight;                                  
            float dividerLeft = parent.getPaddingLeft() + margin;                               
            float dividerBottom = view.getTop();
            floatdividerRight = parent.getWidth() - parent.getPaddingRight() - margin; c.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, mPaint); }}}Copy the code

Use:

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);
Copy the code

In practice we are dp units, so HERE I use the ConvertUtils utility class to convert DP to PX

The code is as follows:

public static int dp2px(Context context, final float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5 f);
    }
Copy the code

Take a look at the effect:

At this point, all the functionality we want is done. (Finally finished code happy!)

If our RecyclerView is horizontal sliding, similar principle, you can do the rest.

Making the address

To summarize

There are two steps:

1. Create an area on top of the itemView with getItemOffsets()

2. Use the onDraw() method to draw a divider of the desired color and offset in the region

In fact, ItemDecoration also has a lot of exciting places, such as the effect of the optical axis when implementing, and the corner marks of the leaderboard. You can see the implementation of the article I referred to, which is very detailed. I’ll update it when THE Demo is written