Use of MeterialDesign_RecyclerView_ItemDecoration

Use of MeterialDesign_RecyclerView_ItemDecoration

Notes on the use of ItemDecration

RecyclerView has no default splitter line and needs to draw itself

Because the linear layout of LayoutParams in RecyclerView is divided into
- LinearLayoutManager
- GridLayoutManager
- StaggeredGridLayoutManager

So write the corresponding split lines for these layouts
-Linear layout corresponds to split lines
-Grid Layout Corresponds to Split Line

Linear layout corresponds to split lines

1. Inherit RecyclerView.ItemDecoration

You will be prompted here to override==getItemOffsets==method and==onDraw==method.
- ==getItemOffsets==The purpose of the method is to set the offset for each Item in the list.
- ==onDraw==The purpose of the method is to draw a line corresponding to each Item.

/**
 * Author:Yang Jianru
 * Time: 2017/9/7  17:18
 * Description: LinearLayoutManager Corresponding RecyclerView splitter lines
 */
public class DeviderItemDecoration extends RecyclerView.ItemDecoration{

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

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

}

2. Write constructors

Pass in the direction that the LinearLayoutManager corresponds to and determine if it belongs to an enumeration in the LinearLayoutManager.

The splitter line is also initialized in the constructor.

    /**
     * The default RecyclerView layout is vertical
     */
    private int mOrientation = LinearLayoutManager.VERTICAL;

    private Drawable mDevider;

    private int[] attrs = new int[]{
            android.R.attr.listDivider
    };

    public DevideItemDecoration(Context mCtx, int orientation){
    //  Get attr get
    //  TypedArray typedArray = mCtx.obtainStyledAttributes(attrs);
    //  mDevider = typedArray.getDrawable(0);
    //  typedArray.recycle();
        setmOrientation(orientation);
        //Get Split Line Picture
        mDevider = mCtx.getResources().getDrawable(R.drawable.shape_devider);
    }

    public void setmOrientation(int mOrientation) {
        if(mOrientation != LinearLayoutManager.HORIZONTAL && mOrientation != LinearLayoutManager.VERTICAL){
            throw new IllegalArgumentException("DevideItemDecoration in Orientation Nonhorizontal and linear enumeration types");
        }
        this.mOrientation = mOrientation;
    }

3. Override getItemOffsets method

In this method, only the offset of the rectangle corresponding to the Item in the settings list is used, that is, the specified value is set to the coordinate of the first parameter "Rect outRect" in the method.

This method is called once per Item during the drawing process.

outRect.set(left, top, right, bottom)

left, top, right, and bottom represent offsets in all directions. Now, of course, when you're writing a dividing line for RecyclerView corresponding to LinearLayoutManager, you can only add a dividing line horizontally or vertically

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

4. Override onDraw method

In fact, the onDraw method is to depict the dividing line in the canvas, which requires judging the layout direction and calculating the position of the dividing line.

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        if (mOrientation == LinearLayoutManager.VERTICAL){
            //TODO: If it is not decided whether it is the last item, the last item's split line is not drawn
            drawVertical(c,parent);
        }else{
            drawHorizontal(c,parent);
        }
    }

    /**
     * Draw horizontal lines
     * @param c
     * @param parent
     */
    private void drawHorizontal(Canvas c, RecyclerView parent) {
        int top = parent.getPaddingTop();
        int bootom = parent.getHeight() - parent.getPaddingBottom();

        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int left = childView.getRight() + layoutParams.rightMargin + Math.round(ViewCompat.getTranslationX(childView));
            int right = left + mDevider.getIntrinsicWidth();
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }

    /**
     * Draw a vertical line
     * @param c
     * @param parent
     */
    private void drawVertical(Canvas c, RecyclerView parent) {
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int top = childView.getBottom() + layoutParams.bottomMargin + Math.round(ViewCompat.getTranslationY(childView));
            int bootom = top + mDevider.getIntrinsicHeight();
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }

4.DeviderItemDecoration full code

Usage

        RecyclerView.addItemDecoration(new DevideItemDecoration(this,LinearLayoutManager.HORIZONTAL));
/**
 * Author:Yang Jianru
 * Time: 2017/9/7  17:18
 * Description: LinearLayoutManager Corresponding RecyclerView splitter lines
 */
public class DeviderItemDecoration extends RecyclerView.ItemDecoration{

    /**
     * The default RecyclerView layout is vertical
     */
    private int mOrientation = LinearLayoutManager.VERTICAL;

    private Drawable mDevider;

    private int[] attrs = new int[]{
            android.R.attr.listDivider
    };

    public DeviderItemDecoration(Context mCtx, int orientation){
//        TypedArray typedArray = mCtx.obtainStyledAttributes(attrs);
//        mDevider = typedArray.getDrawable(0);
//        typedArray.recycle();
//        mDevider = BitmapFactory.decodeResource(mCtx.getResources(),R.drawable.shapeLine);
        setmOrientation(orientation);
        mDevider = mCtx.getResources().getDrawable(R.drawable.shape_line);
    }

    public void setmOrientation(int mOrientation) {
        if(mOrientation != LinearLayoutManager.HORIZONTAL && mOrientation != LinearLayoutManager.VERTICAL){
            throw new IllegalArgumentException("DevideItemDecoration in Orientation Nonhorizontal and linear enumeration types");
        }
        this.mOrientation = mOrientation;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        //1. Call this method (first get the gap width between entries - Rect rectangular area)
        // Gets the offset of the entry (all entries call the method back once)
        if (mOrientation == LinearLayoutManager.VERTICAL){
            outRect.set(0,0,0,mDevider.getIntrinsicHeight());
        }else{
            outRect.set(0,0,mDevider.getIntrinsicWidth(),0);
        }
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        if (mOrientation == LinearLayoutManager.VERTICAL){
            drawVertical(c,parent);
        }else{
            drawHorizontal(c,parent);
        }
    }

    /**
     * Draw horizontal lines
     * @param c
     * @param parent
     */
    private void drawHorizontal(Canvas c, RecyclerView parent) {
        int top = parent.getPaddingTop();
        int bootom = parent.getHeight() - parent.getPaddingBottom();

        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int left = childView.getRight() + layoutParams.rightMargin + Math.round(ViewCompat.getTranslationX(childView));
            int right = left + mDevider.getIntrinsicWidth();
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }

    /**
     * Draw a vertical line
     * @param c
     * @param parent
     */
    private void drawVertical(Canvas c, RecyclerView parent) {
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int top = childView.getBottom() + layoutParams.bottomMargin + Math.round(ViewCompat.getTranslationY(childView));
            int bootom = top + mDevider.getIntrinsicHeight();
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }
}

Table layout corresponds to split lines

A table layout corresponds to a split line, which is actually similar to a linear layout except that the getItemOffsets method is overridden in the table layout.

1. getItemOffsets method for table layout

The offset distance is set in this method.Previously, only offsets were used in linear layouts, and most of the items in a table layout needed offsets in both the horizontal and vertical directions. The last column of items only used offsets in the horizontal direction, and the most row only used offsets in the vertical direction.

Note here that the method where the parameter has itemPosition is required because we are calculating whether it is the last row or column.(There are two such methods for getItemOffsets, and the one with the itemPosition parameter is obsolete.)

@Override
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        super.getItemOffsets(outRect, itemPosition, parent);
        int bottom = mDevider.getIntrinsicWidth();
        int right = mDevider.getIntrinsicHeight();
        if (isLastColum(parent,itemPosition)){
            right = 0;
        }
        if (isLastRow(parent,itemPosition)){
            bottom = 0;
        }

        outRect.set(0,0,right,bottom);
    }

    /**
     * Is this the last line?
     * @param parent
     * @param itemPosition
     * @return
     */
    private boolean isLastRow(RecyclerView parent, int itemPosition) {
        int spanCount = getSpanCount(parent);
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager){
            int childCount = parent.getAdapter().getItemCount();
            int rowCount = childCount%spanCount == 0 ? childCount/spanCount : childCount/spanCount + 1 ;
            itemPosition++;
            int currentRow= itemPosition%spanCount == 0 ? itemPosition/spanCount : itemPosition/spanCount + 1 ;
            if(currentRow == rowCount){
                return true;
            }
        }
        return false;
    }

    /**
     * Determine if it is the last line
     * @param parent
     * @param itemPosition
     * @return
     */
    private boolean isLastColum(RecyclerView parent, int itemPosition) {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager){
            int spanCount = getSpanCount(parent);
            Log.d("isLastColum", itemPosition + "----------" +spanCount);
            if ((itemPosition + 1)%spanCount == 0){
                Log.d("isLastColum", "---true-------");
                return true;
            }
        }
        Log.d("isLastColum", "---false-------");
        return false;
    }

    //Get the number of columns
    private int getSpanCount(RecyclerView parent){
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if(layoutManager instanceof GridLayoutManager){
            GridLayoutManager lm = (GridLayoutManager)layoutManager;
            int spanCount = lm.getSpanCount();
            return spanCount;
        }
        return 0;
    }

2. onDraw method for table layout

Draw the dividing lines, and the horizontal and vertical dividing lines.

//Horizontal not yet done
new DevideGridItemDecoration(this,GridLayoutManager.VERTICAL);
@Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        drawVertical(c,parent);
        drawHorizontal(c,parent);
    }

    /**
     * Draw horizontal lines
     * @param c
     * @param parent
     */
    private void drawHorizontal(Canvas c, RecyclerView parent) {

        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int left = childView.getLeft() - layoutParams.leftMargin;
            int top = childView.getBottom() + layoutParams.bottomMargin;
            int right = childView.getRight() + layoutParams.rightMargin;
            int bootom = top + mDevider.getIntrinsicHeight();
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }

    /**
     * Draw a vertical line
     * @param c
     * @param parent
     */
    private void drawVertical(Canvas c, RecyclerView parent) {
        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int left = childView.getRight() +  layoutParams.rightMargin;
            int top = childView.getTop() - layoutParams.topMargin;
            int right = left + mDevider.getIntrinsicWidth();
            int bootom = childView.getBottom() + layoutParams.bottomMargin;
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }

3.DeviderGridItemDecoration code

The horizontal aspect of the table layout does not handle the same effect and is not done.Have time to talk

/**
 * Author:Yang Jianru
 * Time: 2017/9/7  17:18
 * Description: GridLayoutManager Corresponding RecyclerView splitter lines
 */
public class DevideGridItemDecoration extends RecyclerView.ItemDecoration{

    /**
     * The default RecyclerView layout is vertical
     */
    private int mOrientation = LinearLayoutManager.VERTICAL;

    private Drawable mDevider;

    private int[] attrs = new int[]{
            android.R.attr.listDivider
    };

    public DevideGridItemDecoration(Context mCtx, int orientation){
//        TypedArray typedArray = mCtx.obtainStyledAttributes(attrs);
//        mDevider = typedArray.getDrawable(0);
//        typedArray.recycle();
        setmOrientation(orientation);
        mDevider = mCtx.getResources().getDrawable(R.drawable.shape_line);
    }

    public void setmOrientation(int mOrientation) {
        if(mOrientation != LinearLayoutManager.HORIZONTAL && mOrientation != LinearLayoutManager.VERTICAL){
            throw new IllegalArgumentException("DevideItemDecoration in Orientation Nonhorizontal and linear enumeration types");
        }
        this.mOrientation = mOrientation;
    }

    @Override
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        super.getItemOffsets(outRect, itemPosition, parent);
        int bottom = mDevider.getIntrinsicWidth();
        int right = mDevider.getIntrinsicHeight();
        if (mOrientation == GridLayoutManager.VERTICAL){
            if (isLastColum(parent,itemPosition)){
                right = 0;
            }
            if (isLastRow(parent,itemPosition)){
                bottom = 0;
            }
        }else{
            if (isLastColum(parent,itemPosition)){
                Log.d("isLastColum", "---bottom = 0;------");
                bottom = 0;
            }
            if (isLastRow(parent,itemPosition)){
                right = 0;
            }
        }

        outRect.set(0,0,right,bottom);
    }

    /**
     * Is this the last line?
     * @param parent
     * @param itemPosition
     * @return
     */
    private boolean isLastRow(RecyclerView parent, int itemPosition) {
        int spanCount = getSpanCount(parent);
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager){
            int childCount = parent.getAdapter().getItemCount();
            int rowCount = childCount%spanCount == 0 ? childCount/spanCount : childCount/spanCount + 1 ;
            itemPosition++;
            int currentRow= itemPosition%spanCount == 0 ? itemPosition/spanCount : itemPosition/spanCount + 1 ;
            if(currentRow == rowCount){
                return true;
            }
        }
        return false;
    }

    /**
     * Determine if it is the last line
     * @param parent
     * @param itemPosition
     * @return
     */
    private boolean isLastColum(RecyclerView parent, int itemPosition) {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager){
            int spanCount = getSpanCount(parent);
            Log.d("isLastColum", itemPosition + "----------" +spanCount);
            if ((itemPosition + 1)%spanCount == 0){
                Log.d("isLastColum", "---true-------");
                return true;
            }
        }
        Log.d("isLastColum", "---false-------");
        return false;
    }

    //Get the number of columns
    private int getSpanCount(RecyclerView parent){
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if(layoutManager instanceof GridLayoutManager){
            GridLayoutManager lm = (GridLayoutManager)layoutManager;
            int spanCount = lm.getSpanCount();
            return spanCount;
        }
        return 0;
    }


    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
//        if (mOrientation == LinearLayoutManager.VERTICAL){
//            drawVertical(c,parent);
//            drawHorizontal(c,parent);
//        }else{
//        }
        drawVertical(c,parent);
        drawHorizontal(c,parent);
    }

    /**
     * Draw horizontal lines
     * @param c
     * @param parent
     */
    private void drawHorizontal(Canvas c, RecyclerView parent) {

        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int left = childView.getLeft() - layoutParams.leftMargin;
            int top = childView.getBottom() + layoutParams.bottomMargin;
            int right = childView.getRight() + layoutParams.rightMargin;
            int bootom = top + mDevider.getIntrinsicHeight();
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }

    /**
     * Draw a vertical line
     * @param c
     * @param parent
     */
    private void drawVertical(Canvas c, RecyclerView parent) {
        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int left = childView.getRight() +  layoutParams.rightMargin;
            int top = childView.getTop() - layoutParams.topMargin;
            int right = left + mDevider.getIntrinsicWidth();
            int bootom = childView.getBottom() + layoutParams.bottomMargin;
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }
}

Other uses

  • Draw watermarks on controls
  • Add a tag to the side of the control

Keywords: Android

Added by generic on Mon, 27 May 2019 00:48:46 +0300