简体   繁体   English

recyclerview中基于viewtype的ItemDecoration

[英]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.我的RecyclerView有多种视图类型,我想根据视图类型添加一个ItemDecoration 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.我看到了这个库,但它只支持LinearLayoutManager垂直或水平,但我使用的是GrildLayoutManager并且我使用 drawables 作为分隔线。

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:如果您自己绘制装饰,您可以通过访问适配器上的相同方法来区分getItemOffsetsonDraw的不同视图类型,如下所示:

// 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.通过访问代码支持GridLayoutLinearLayout getLeft()getRight()来支持水平对齐,只需使用相同的方法在右侧完成绘图。

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. GitHub 上有一个类似的示例,带有一个演示项目,不会在标题视图之前或之后或最后绘制。

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,基于@David MedenJak 的回答,我为不同的视图类型制作了我自己的项目装饰器,因为当它在任何正常行之后绘制装饰器时,答案在一种情况下滞后,

    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, MRecyclerListItem.TYPE_NORMAL 是您的正常行(标题除外)的视图类型在以下管理器中调用上述内容,

 mRecyclerview.addItemDecoration(new HeaderSimpleDividerDecoration(context,
            2dp , R.color.view_profile_edit_view));

Here's a shorter code to draw divider for certain view type only that can be easily included in your activity/fragment class:这是一个较短的代码,用于为某些视图类型绘制分隔线,可以轻松包含在您的活动/片段类中:

recyclerView.addItemDecoration(new DividerItemDecoration(this, linearLayoutManager.getOrientation()) {
        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            Drawable d = getDrawable();
            for (int i = 0; i < parent.getChildCount(); i++) {
                View view = parent.getChildAt(i);
                int position = parent.getChildAdapterPosition(view);
                int viewType = parent.getAdapter().getItemViewType(position);
                
                // Draw divider only for view type 2 (can also put position here to remove for certain positions)
                if(viewType == 2) {
                    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
                    int top = view.getBottom() + params.bottomMargin;
                    int bottom = top + d.getIntrinsicHeight();
                    d.setBounds(0, top, parent.getRight(), bottom);
                    d.draw(c);
                }
            }
        }
    });

Hy people !嗨人!

Example using 2 Drawable separators for Horizontal ItemSeparator based on items view type :基于项目视图类型为 Horizo​​ntal ItemSeparator使用 2 Drawable分隔符的示例:

override fun getItemOffsets(...) {
    parent.adapter?.let { adapter ->
        val childAdapterPosition = parent.getChildAdapterPosition(view)
            .let { if (it == RecyclerView.NO_POSITION) return else it }

        rect.right = when (adapter.getItemViewType(childAdapterPosition)) {
            YourAdapter.FIRST_ITEM_ID -> firstSeparator.intrinsicWidth
            YourAdapter.SECOND_ITEM_ID -> secondSeparator.intrinsicWidth
            else -> 0
        }
    }
}

override fun onDraw(...) {
    parent.adapter?.let { adapter ->
        parent.children
            .forEach { view ->
                val childAdapterPosition = parent.getChildAdapterPosition(view)
                    .let { if (it == RecyclerView.NO_POSITION) return else it }

                when (adapter.getItemViewType(childAdapterPosition)) {
                    CustomAdapter.FIRST_ITEM_ID -> firstSeparator.draw(...)
                    CustomAdapter.SECOND_ITEM_ID -> secondSeparator.draw(...)
                    else -> Unit
                }
            }
    }
}

private fun Drawable.draw(view: View, parent: RecyclerView, canvas: Canvas) = apply {
    val left = view.right
    val top = parent.paddingTop
    val right = left + intrinsicWidth
    val bottom = top + intrinsicHeight - parent.paddingBottom
    bounds = Rect(left, top, right, bottom)
    draw(canvas)
}

For more info on ItemSeparator , Here is the article I wrote to explain how to build your own custom ItemDecoration.有关ItemSeparator更多信息, 是我写的文章,用于解释如何构建您自己的自定义 ItemDecoration。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM