【问题标题】:How to avoid double space between items when using RecyclerView with StaggeredGridLayoutManager?使用带有 StaggeredGridLayoutManager 的 RecyclerView 时如何避免项目之间的双倍空格?
【发布时间】:2015-05-08 21:50:15
【问题描述】:

我正在使用 RecyclerView 和 StaggeredGridLayoutManager 来制作一个两列的列表。但是如何在左列和右列之间设置右边距。我已经使用此代码从顶部制作右边距,但是如何解决列之间的双重空格。

public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
    private int space;

    public SpacesItemDecoration(int space) {
        this.space = space;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        outRect.left = space;
        outRect.right = space;
        outRect.bottom = space;

        // Add top margin only for the first or second item to avoid double space between items
        // Add top margin only for the first or second item to avoid double space between items
        if((parent.getChildCount() > 0 && parent.getChildPosition(view) == 0)
            || (parent.getChildCount() > 1 && parent.getChildPosition(view) == 1))
        outRect.top = space;
}

在活动中:

recyclerView.addItemDecoration(new SpacesItemDecoration(20));

我尝试使用view.getX(),它总是返回0。

谁能帮帮我?非常感谢!

【问题讨论】:

    标签: android android-recyclerview margins staggeredgridlayout


    【解决方案1】:

    我自己通过设置项目边距和 RecyclerView 填充解决了这个问题。 margin 和 padding 都是预期空间的一半,所以问题消失了。

    【讨论】:

      【解决方案2】:

      以下代码将处理 StaggeredGridLayoutManager、GridLayoutManager 和 LinearLayoutManager。

      public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
      
          private int halfSpace;
      
          public SpacesItemDecoration(int space) {
              this.halfSpace = space / 2;
          }
      
          @Override
          public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
      
              if (parent.getPaddingLeft() != halfSpace) {
                  parent.setPadding(halfSpace, halfSpace, halfSpace, halfSpace);
                  parent.setClipToPadding(false);
              }
      
              outRect.top = halfSpace;
              outRect.bottom = halfSpace;
              outRect.left = halfSpace;
              outRect.right = halfSpace;
          }
      }
      

      【讨论】:

        【解决方案3】:

        在您的情况下,您可以将两个边距设置为 RecyclerView 。但在我的情况下,RecyclerView 中有一个与屏幕宽度匹配的图像,我使用 ItemDecoration 来解决问题。

          class ViewItemDecoration extends RecyclerView.ItemDecoration {
        
            public ViewItemDecoration() {
            }
        
            @Override
            public void getItemOffsets(Rect outRect, final View view, final RecyclerView parent, RecyclerView.State state) {
                super.getItemOffsets(outRect, view, parent, state);
                int position = parent.getChildAdapterPosition(view);
                int spanIndex = ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
                int type = adapter.getItemViewType(position);
                switch(type){
                  case YOUR_ITEMS:
                        if (spanIndex == 0) {
                            outRect.left = 10;
                            outRect.right = 5;
                        } else {//if you just have 2 span . Or you can use (staggeredGridLayoutManager.getSpanCount()-1) as last span
                            outRect.left = 5;
                            outRect.right = 10;
                        }
                }
            }
        
        }
        

        【讨论】:

          【解决方案4】:

          我会稍微改变和修改相关问题的两个答案

          public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
              private final int mSpace;
          
              public SpacesItemDecoration(int space) {
                  this.mSpace = space;
              }
          
              @Override
              public void getItemOffsets(Rect outRect, final View view, final RecyclerView parent, RecyclerView.State state) {
                  super.getItemOffsets(outRect, view, parent, state);
                  int position = parent.getChildAdapterPosition(view);
                  int spanIndex = ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
                  if (spanIndex == 0) {
                      outRect.left = 30;
                      outRect.right = 15;
                  } else {//if you just have 2 span . Or you can use (staggeredGridLayoutManager.getSpanCount()-1) as last span
                      outRect.left = 15;
                      outRect.right = 30;
                  }
                  outRect.bottom = 30;
                  // Add top margin only for the first item to avoid double space between items
                  if (parent.getChildAdapterPosition(view) == 0) {
                      outRect.top = 30;
                      outRect.right = 30;
                  }
              }
          }
          

          【讨论】:

            【解决方案5】:

            根据 spanindex 在 ItemDecoration 中设置不同的左/右空间仅适用于具有固定大小的项目视图。 如果 item view 包含 image view set wrap_content in height,item view 可能会改变 span 位置,但是 span index 没有按照你的意愿更新,margin 会很乱。

            我的方法是,使用左右对称的空间来查看项目。

            @Override
            public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
                // init
                recyclerView = (RecyclerView) rv.findViewById(R.id.img_waterfall);
                layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
                recyclerView.setLayoutManager(layoutManager);
                recyclerView.setAdapter(new MyAdapter(getContext()));
                recyclerView.addItemDecoration(new SpacesItemDecoration(
                    getResources().getDimensionPixelSize(R.dimen.space)));
            }
            
            
            private static class SpacesItemDecoration extends RecyclerView.ItemDecoration {
                private final int space;
                public SpacesItemDecoration(int space) {
                    this.space = space;
                }
                @Override
                public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
                                           RecyclerView.State state) {
                    outRect.bottom = 2 * space;
                    int pos = parent.getChildAdapterPosition(view);
                    outRect.left = space;
                    outRect.right = space;
                    if (pos < 2)
                        outRect.top = 2 * space;
                }
            }
            

            然后为 RecylerView(left/right) 设置相同的 padding android:paddingLeft="@dimen/space" android:paddingRight="@dimen/space"

                <android.support.v7.widget.RecyclerView
                    android:id="@+id/img_waterfall"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:paddingLeft="@dimen/space"
                    android:paddingRight="@dimen/space"/>
            

            【讨论】:

              【解决方案6】:
              public class EqualGapItemDecoration extends RecyclerView.ItemDecoration {
              
                  private int spanCount;
                  private int spacing;
              
                  public EqualGapItemDecoration(int spanCount, int spacing) {
                      this.spanCount = spanCount;
                      this.spacing = spacing;
                  }
              
                  @Override
                  public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
              
                      StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
              
                      if (layoutParams.isFullSpan()) {
                          outRect.set(0, 0, 0, 0);
                      } else {
                          int spanIndex = layoutParams.getSpanIndex();
                          int layoutPosition = layoutParams.getViewLayoutPosition();
                          int itemCount = parent.getAdapter().getItemCount();
              
                          boolean leftEdge = spanIndex == 0;
                          boolean rightEdge = spanIndex == (spanCount - 1);
              
                          boolean topEdge = spanIndex < spanCount;
                          boolean bottomEdge = layoutPosition >= (itemCount - spanCount);
              
                          int halfSpacing = spacing / 2;
              
                          outRect.set(
                                  leftEdge ? spacing : halfSpacing,
                                  topEdge ? spacing : halfSpacing,
                                  rightEdge ? spacing : halfSpacing,
                                  bottomEdge ? spacing : 0
                          );
                      }
                  }
              }
              

              【讨论】:

                【解决方案7】:

                可以在RecyclerView的两侧设置padding:

                myRecyclerView.setPadding(10,0,10,0);
                

                通过这种方式,您可以平衡两侧和列之间的空间。

                【讨论】:

                  【解决方案8】:

                  我通过以下方式分 3 步解决了这个问题:

                  1. 将marginStart和marginEnd添加到具有相同值的父级(RecyclerView)
                  <androidx.recyclerview.widget.RecyclerView
                      android:id="@+id/recyclerView"
                      android:layout_width="match_parent"
                      android:layout_height="match_parent"
                      android:layout_marginStart="@dimen/margin_small"
                      android:layout_marginEnd="@dimen/margin_small"
                      app:layoutManager="androidx.recyclerview.widget.StaggeredGridLayoutManager"
                      app:spanCount="2" />
                  
                  1. 创建自定义 ItemDecorator 类并扩展 RecyclerView.ItemDecoratior。获取marigns parent,然后根据情况修改每个child一半的内部边距
                  class SpaceItemDecorator : RecyclerView.ItemDecoration() {
                  
                      override fun getItemOffsets(
                          outRect: Rect,
                          view: View,
                          parent: RecyclerView,
                          state: RecyclerView.State
                      ) {
                          super.getItemOffsets(outRect, view, parent, state)
                  
                          val layoutParams = view.layoutParams as StaggeredGridLayoutManager.LayoutParams
                          val spanIndex = layoutParams.spanIndex
                  
                          if (spanIndex == 0) {
                              val marginParentEnd = parent.marginEnd
                              layoutParams.marginEnd = marginParentEnd / 2
                          } else {
                              val marginParentStart = parent.marginStart
                              layoutParams.marginStart = marginParentStart / 2
                          }
                  
                          view.layoutParams = layoutParams
                      }
                  }
                  
                  1. 将自定义ItemDecorator添加到recyclerView
                  recyclerview.addItemDecoration(SpaceItemDecorator())
                  

                  就是这样,没有边距/填充硬编码和在 Koltin xD 中

                  注意:重要的是不要在子视图布局中建立任何水平边距

                  【讨论】:

                    【解决方案9】:

                    以下代码可以毫无问题地处理 StaggeredGridLayout(至少根据我的经验)。您只需要为 SpaceItemDecorator 提供间距。

                    class SpaceItemDecoration(private val spacing: Int) :
                        RecyclerView.ItemDecoration() {
                    
                        override fun getItemOffsets(
                            outRect: Rect,
                            view: View,
                            parent: RecyclerView,
                            state: RecyclerView.State
                        ) {
                    
                            val lm = parent.layoutManager as StaggeredGridLayoutManager
                            val lp = view.layoutParams as StaggeredGridLayoutManager.LayoutParams
                    
                            val spanCount = lm.spanCount
                            val spanIndex = lp.spanIndex
                            val positionLayout = lp.viewLayoutPosition
                            val itemCount = lm.itemCount
                            val position = parent.getChildAdapterPosition(view)
                    
                            outRect.right = spacing / 2
                            outRect.left = spacing / 2
                            outRect.top = spacing / 2
                            outRect.bottom = spacing / 2
                    
                            if (spanIndex == 0) outRect.left = spacing
                    
                            if (position < spanCount) outRect.top = spacing
                    
                            if (spanIndex == (spanCount - 1)) outRect.right = spacing
                    
                            if (positionLayout > (itemCount - spanCount)) outRect.bottom = spacing
                        }
                    }
                    

                    您可以像这样以像素为单位获得间距:

                     val spacingInPixels = resources.getDimensionPixelSize(R.dimen.grid_layout_margin)
                    

                    然后你只需要将物品装饰分配给你的回收站视图:

                    recycler_view.addItemDecoration(
                                      SpaceItemDecoration(
                                            spacingInPixels
                                        )
                                    )
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2020-05-25
                      • 1970-01-01
                      • 1970-01-01
                      • 2021-06-28
                      • 1970-01-01
                      • 2016-07-19
                      • 1970-01-01
                      相关资源
                      最近更新 更多