【问题标题】:RecyclerView with GridLayoutManager trying to solve wrap_contentRecyclerView 与 GridLayoutManager 试图解决 wrap_content
【发布时间】:2015-07-28 15:54:00
【问题描述】:

我尝试解决以下问题:

  • 使用 RecyclerView
  • 使用 GridLayoutManager
  • 固定单元格宽度
  • recyclerview 仅将大小调整到必要的高度 (wrap_content)

我尝试使用以下代码,问题是它不起作用,并且我没有找到任何工作示例没有提到在方向更改时正确重绘。

public class AutofitRecyclerView extends RecyclerView {
    private MyGridLayoutManager manager;
    private int columnWidth = -1;

    public AutofitRecyclerView(Context context) {
        super(context);
        init(context, null);
    }

    public AutofitRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public AutofitRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        if (attrs != null) {
            int[] attrsArray = {
                    android.R.attr.columnWidth
            };
            TypedArray array = context.obtainStyledAttributes(attrs, attrsArray);
            columnWidth = array.getDimensionPixelSize(0, -1);
            array.recycle();
        }

        manager = new MyGridLayoutManager(getContext(), 1);
        setLayoutManager(manager);
    }

    @Override
    protected void onMeasure(int widthSpec, int heightSpec) {
        super.onMeasure(widthSpec, heightSpec);
        if (columnWidth > 0) {

            int mw = getMeasuredWidth();

            int spanCount = Math.max(1, mw / columnWidth);
            manager.setSpanCount(spanCount);
        }

        int numOfFullRows = manager.getItemCount() / manager.getSpanCount();

        if(manager.getItemCount() % manager.getSpanCount() > 0)
        {
            numOfFullRows++;
        }

        if(getChildCount() > 0) {
            int h = 0;
            try {
                h = manager.getChildAt(0).getMeasuredHeight();
            } catch (Exception e) {
                e.printStackTrace();
            }

            if(h != 0) {
                getLayoutParams().height = numOfFullRows * h;
            }
        }
    }
}

【问题讨论】:

    标签: android android-recyclerview gridlayoutmanager


    【解决方案1】:

    根据连接器发布的代码,我提出了以下解决方案,它支持不同的行/列大小(尽管我承认没有测试过),考虑到项目装饰的大小并正确处理不同的测量模式.

    代码如下:

    import android.content.Context;
    import android.graphics.Rect;
    import android.support.v7.widget.GridLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.view.View;
    import android.view.ViewGroup;
    
    public class WrappableGridLayoutManager extends GridLayoutManager {
    
        public WrappableGridLayoutManager(Context context, int spanCount) {
            super(context, spanCount);
        }
    
        private int[] measuredSize = new int[2];
    
        @Override
        public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
            final int widthMode = View.MeasureSpec.getMode(widthSpec);
            final int heightMode = View.MeasureSpec.getMode(heightSpec);
            final int widthSize = View.MeasureSpec.getSize(widthSpec);
            final int heightSize = View.MeasureSpec.getSize(heightSpec);
    
            int spanWidth = 0;
            int spanHeight = 0;
    
            int viewWidth = 0;
            int viewHeight = 0;
    
            int spanCount = getSpanCount();
    
            for (int i = 0; i < getItemCount(); i++) {
    
                measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), measuredSize);
    
                if (i % spanCount == 0) {
                    spanWidth = measuredSize[0];
                    spanHeight = measuredSize[1];
                } else {
                    if (getOrientation() == VERTICAL) {
                        spanWidth += measuredSize[0];
                        spanHeight = Math.max(spanHeight, measuredSize[1]);
                    } else {
                        spanWidth = Math.max(spanWidth, measuredSize[0]);
                        spanHeight += measuredSize[1];
                    }
                }
    
                if (i % spanCount == spanCount - 1 || i == getItemCount() - 1) {
                    if (getOrientation() == VERTICAL) {
                        viewWidth = Math.max(viewWidth, spanWidth);
                        viewHeight += spanHeight;
                    } else {
                        viewWidth += spanWidth;
                        viewHeight = Math.max(viewHeight, spanHeight);
                    }
                }
            }
    
            int finalWidth;
            int finalHeight;
    
            switch (widthMode) {
                case View.MeasureSpec.EXACTLY:
                    finalWidth = widthSize;
                    break;
                case View.MeasureSpec.AT_MOST:
                    finalWidth = Math.min(widthSize, viewWidth);
                    break;
                case View.MeasureSpec.UNSPECIFIED:
                    finalWidth = viewWidth;
                    break;
                default:
                    finalWidth = widthSize;
                    break;
            }
    
            switch (heightMode) {
                case View.MeasureSpec.EXACTLY:
                    finalHeight = heightSize;
                    break;
                case View.MeasureSpec.AT_MOST:
                    finalHeight = Math.min(heightSize, viewHeight);
                    break;
                case View.MeasureSpec.UNSPECIFIED:
                    finalHeight = viewHeight;
                    break;
                default:
                    finalHeight = heightSize;
                    break;
            }
    
            setMeasuredDimension(finalWidth, finalHeight);
        }
    
    
        private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) {
    
            View view = null;
            try {
                view = recycler.getViewForPosition(position);
            } catch (Exception ex) {
              // try - catch is needed since support library version 24
            }
    
    
            if (view != null) {
    
                RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
    
                int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), p.width);
                int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), p.height);
    
                view.measure(childWidthSpec, childHeightSpec);
    
                measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
                measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
    
                Rect decoratorRect = new Rect();
                calculateItemDecorationsForChild(view, decoratorRect);
                measuredDimension[0] += decoratorRect.left;
                measuredDimension[0] += decoratorRect.right;
                measuredDimension[1] += decoratorRect.top;
                measuredDimension[1] += decoratorRect.bottom;
    
                recycler.recycleView(view);
            }
        }
    }
    

    【讨论】:

    • 为什么会被否决?这是连接器代码的改进版本,是从我为工作的、已发布的应用程序编写的代码中复制而来的,所以我知道它可以工作。
    • downvotes 可能来自人们,他们可能只有一项,例如2 列 - 在这种情况下,您的经理将回收站视图的高度设置为 0(6.0 Nexus 7)。上面其他答案中的 git 链接运行良好(我没有使用装饰)
    • 感谢您告诉我。有时间我会尽快调查的。
    • 更新了我的答案并解决了这个问题。
    • 用 Android SDK 24 的修复更新了我的答案。
    【解决方案2】:

    您需要添加一个自定义 GridlayoutManager。 检查这个https://gist.github.com/ArthurSav/5f80e19d9ba6d562fbd5

    【讨论】:

      【解决方案3】:

      感谢@nantoka 和@arthur 提供代码,它给了我一个良好的开端。不幸的是,它不适用于不同的 spanCount,所以我在这里是我的更新(Kotlin):

      import android.content.Context
      import android.util.AttributeSet
      import android.view.View
      import android.view.ViewGroup
      import androidx.recyclerview.widget.GridLayoutManager
      import androidx.recyclerview.widget.RecyclerView
      import androidx.recyclerview.widget.RecyclerView.Recycler
      import com.baziliqo.app.bar.utils.takeIfInstance
      
      class WrappableGridLayoutManager(context: Context?, val preferedSpanCount: Int, @RecyclerView.Orientation orientation: Int = RecyclerView.VERTICAL) :
          GridLayoutManager(context, preferedSpanCount, orientation, false) {
      
          private val mMeasuredDimension = IntArray(2)
      
          var measuredWidth = 0
          var measuredHeight = 0
      
      
          override fun onMeasure(recycler: Recycler, state: RecyclerView.State, widthSpec: Int, heightSpec: Int) {
              val suitableSpanCount = preferedSpanCount.coerceAtMost(itemCount)
              if (spanCount != suitableSpanCount) {
                  spanCount = suitableSpanCount
                  return
              }
              val widthMode = View.MeasureSpec.getMode(widthSpec)
              val heightMode = View.MeasureSpec.getMode(heightSpec)
              val widthSize = View.MeasureSpec.getSize(widthSpec)
              val heightSize = View.MeasureSpec.getSize(heightSpec)
              measuredWidth = 0
              measuredHeight = 0
              for (i in 0 until itemCount) {
                  measureScrapChild(
                      recycler, i,
                      View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                      View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                      mMeasuredDimension
                  )
                  if (orientation == HORIZONTAL) {
                      if (i % spanCount == 0) {
                          measuredWidth += mMeasuredDimension[0]
                      }
                      if (i < spanCount) {
                          measuredHeight += mMeasuredDimension[1]
                      }
                  } else {
                      if (i % spanCount == 0) {
                          measuredHeight += mMeasuredDimension[1]
                      }
                      if (i < spanCount) {
                          measuredWidth += mMeasuredDimension[0]
                      }
                  }
              }
              when (widthMode) {
                  View.MeasureSpec.EXACTLY -> measuredWidth = widthSize
                  View.MeasureSpec.AT_MOST, View.MeasureSpec.UNSPECIFIED -> {
                  }
              }
              when (heightMode) {
                  View.MeasureSpec.EXACTLY -> measuredHeight = heightSize
                  View.MeasureSpec.AT_MOST, View.MeasureSpec.UNSPECIFIED -> {
                  }
              }
              setMeasuredDimension(measuredWidth, measuredHeight)
          }
      
          private fun measureScrapChild(recycler: Recycler, position: Int, widthSpec: Int, heightSpec: Int, measuredDimension: IntArray) {
              try {
                  var view = recycler.getViewForPosition(position) ?: return
                  val p = view.layoutParams as RecyclerView.LayoutParams
                  val childWidthSpec = ViewGroup.getChildMeasureSpec(
                      widthSpec,
                      paddingLeft + paddingRight, p.width
                  )
                  val childHeightSpec = ViewGroup.getChildMeasureSpec(
                      heightSpec,
                      paddingTop + paddingBottom, p.height
                  )
                  view.measure(childWidthSpec, childHeightSpec)
                  measuredDimension[0] = view.measuredWidth + p.leftMargin + p.rightMargin
                  measuredDimension[1] = view.measuredHeight + p.bottomMargin + p.topMargin
                  recycler.recycleView(view)
              } catch (e: Exception) {
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2015-08-24
        • 1970-01-01
        • 2017-01-16
        • 2015-01-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-07
        • 1970-01-01
        相关资源
        最近更新 更多