【问题标题】:ItemDecoration based on viewtype in recyclerview在recyclerview中基于viewtype的ItemDecoration
【发布时间】:2016-05-02 23:44:21
【问题描述】:

我的RecyclerView 中有多个视图类型,我想根据视图类型添加一个ItemDecoration。有没有办法做到这一点?

这将为每个元素添加一个装饰:

recyclerView.addItemDecoration(decoration);

我看到了这个library,但它只支持LinearLayoutManager 垂直或水平,但我正在使用GrildLayoutManager 并且我使用drawables 作为分隔符。

【问题讨论】:

    标签: java android android-recyclerview


    【解决方案1】:

    是的,你可以。

    如果你自己绘制装饰,你可以像这样访问适配器上的相同方法来区分getItemOffsetsonDraw中不同的视图类型:

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

    使用它,您可以只为您选择的视图绘制装饰。通过访问代码支持GridLayoutLinearLayoutgetLeft()getRight(),以支持水平 对齐,只需使用相同的方法在右侧完成绘图。

    最后,您将创建如下装饰:

    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);
                }
            }
        }
    }
    

    有一个类似sample on GitHub 的演示项目,它不会在标题视图之前或之后或最后绘制。

    【讨论】:

    • @user3531883 我认为您需要更详细。上面的代码将帮助您区分不同的视图类型,您可以据此绘制您的装饰。
    • 如何根据viewtype绘制我的装饰
    • @user3531883 这正是上面的 sn-p 要做的。 if 条件取决于视图类型。
    • @user3531883 我添加了一个完整的例子
    • 你不应该在装饰中访问适配器,适配器的观点可能不是屏幕上的内容(参见google io talk youtu.be/LqBlYJTfLP4?t=2317),将所有必要的信息保留在视图持有者(访问它与#getChildViewHolder)。
    【解决方案2】:

    基于@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 是您的普通行的视图类型(标题除外) 在以下经理中调用上述内容,

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

    【讨论】:

    【解决方案3】:

    这里有一个更短的代码,仅用于为某些视图类型绘制分隔线,可以轻松地包含在您的活动/片段类中:

    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);
                    }
                }
            }
        });
    

    【讨论】:

      【解决方案4】:

      大家好!

      根据项目视图类型使用 2 个Drawable 水平分隔符ItemSeparator 的示例:

      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)
      }
      

      有关ItemSeparator 的更多信息,Here 是我写的文章,用于解释如何构建您自己的自定义 ItemDecoration。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-12-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-21
        • 2019-03-01
        • 1970-01-01
        • 2019-02-25
        相关资源
        最近更新 更多