Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ItemDecoration based on viewtype in recyclerview

I have multiple view types in my RecyclerView and I want to add an ItemDecoration based on the views type. Is there a way to do this?

This will add a decoration to every element:

recyclerView.addItemDecoration(decoration);

I saw this library but it supports only LinearLayoutManager vertical or horizontal, but I am using GrildLayoutManager and I use drawables for dividers.

like image 662
user3531883 Avatar asked Jan 26 '16 11:01

user3531883


People also ask

What is RecyclerView ItemDecoration?

An ItemDecoration allows the application to add a special drawing and layout offset to specific item views from the adapter's data set. This can be useful for drawing dividers between items, highlights, visual grouping boundaries and more.


2 Answers

Yes, you can.

If you draw the decoration yourself, you can distinguish between different view types in getItemOffsets and onDraw by accessing the same method on the adapter like this:

// get the position
int position = parent.getChildAdapterPosition(view);
// get the view type
int viewType = parent.getAdapter().getItemViewType(position);

Using this, you can draw your decoration only for your selected views. By accessing getLeft() and getRight() that code supports GridLayout as well as LinearLayout, to support horizontal alignment, the drawing just has to be done on the right side using the same approach.

In the end, you would create a decoration like the following:

public class DividerDecoration extends RecyclerView.ItemDecoration {

    private final Paint mPaint;
    private int mHeightDp;

    public DividerDecoration(Context context) {
        this(context, Color.argb((int) (255 * 0.2), 0, 0, 0), 1f);
    }

    public DividerDecoration(Context context, int color, float heightDp) {
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(color);
        mHeightDp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, heightDp, context.getResources().getDisplayMetrics());
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        int position = parent.getChildAdapterPosition(view);
        int viewType = parent.getAdapter().getItemViewType(position);
        if (viewType == MY_VIEW_TYPE) {
            outRect.set(0, 0, 0, mHeightDp);
        } else {
            outRect.setEmpty();
        }
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        for (int i = 0; i < parent.getChildCount(); i++) {
            View view = parent.getChildAt(i);
            int position = parent.getChildAdapterPosition(view);
            int viewType = parent.getAdapter().getItemViewType(position);
            if (viewType == MY_VIEW_TYPE) {
                c.drawRect(view.getLeft(), view.getBottom(), view.getRight(), view.getBottom() + mHeightDp, mPaint);
            }
        }
    }
}

There is a similar sample on GitHub with a demo project, which will not draw before or after header views or at the very end.

like image 80
David Medenjak Avatar answered Oct 06 '22 03:10

David Medenjak


Based on @David MedenJak answer, I had made my own Item Decorator for different view types as the answer lag in one condition as it draws decorator above section, if it comes after any normal row,

    import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.ColorRes;
import android.support.annotation.DimenRes;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import java.util.Locale;

import mp.data.modal.MRecyclerListItem;

public class HeaderSimpleDividerDecoration extends RecyclerView.ItemDecoration {

    private int                 dividerHeight;
    private Paint               dividerPaint;

    public HeaderSimpleDividerDecoration(Context context, @DimenRes int divider_height, @ColorRes int color) {
        dividerPaint = new Paint();
        dividerPaint.setColor(getColor(context, color));
        dividerHeight = context.getResources().getDimensionPixelSize(divider_height);
    }

    private int getColor(Context context, @ColorRes int drawable) {
        return ContextCompat.getColor(context, drawable);
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        int position = parent.getChildAdapterPosition(view);
        if(-1 >= position)
            return;
        int viewType = parent.getAdapter().getItemViewType(position);

        if (MRecyclerListItem.TYPE_NORMAL == viewType) {
            // outRect.set(0, 0, 0, mHeightDp);
                outRect.bottom = dividerHeight;
        } else
            outRect.setEmpty();
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int childCount = parent.getChildCount() -1;
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int top = parent.getPaddingTop();
        int bottom = parent.getHeight() - parent.getPaddingBottom();
        int itemCount = parent.getAdapter().getItemCount();

        for (int i = 0; i < childCount ; i++) {
            View view = parent.getChildAt(i);

            int position = parent.getChildAdapterPosition(view);
            int viewType = parent.getAdapter().getItemViewType(position);

            if (MRecyclerListItem.TYPE_NORMAL == viewType) {
                    int nextItem = position + 1;
                    if(nextItem < itemCount)
                    {
                        int nextViewType = parent.getAdapter().getItemViewType(nextItem);
                        if(MRecyclerListItem.TYPE_NORMAL != nextViewType)
                            continue;
                    }

                    float topDraw = view.getBottom();
                    float bottomDraw = view.getBottom() + dividerHeight;

                    c.drawRect(left, topDraw, right, bottomDraw, dividerPaint);
                }
            }

        }
    }

MRecyclerListItem.TYPE_NORMAL is your view type of normal row(other than header) call the above in following manager,

 mRecyclerview.addItemDecoration(new HeaderSimpleDividerDecoration(context,
            2dp , R.color.view_profile_edit_view));
like image 29
Reprator Avatar answered Oct 06 '22 02:10

Reprator