RecyclerView with GridLayoutManager trying to solve wrap_content

前端 未结 3 1677
猫巷女王i
猫巷女王i 2020-12-16 21:33

I try to solve the following:

  • using RecyclerView
  • with GridLayoutManager
  • with fixed cell-widths
  • recyclerview resizing only to the nec
相关标签:
3条回答
  • 2020-12-16 22:00

    You'll need to add a custom GridlayoutManager. Check this https://gist.github.com/ArthurSav/5f80e19d9ba6d562fbd5

    0 讨论(0)
  • 2020-12-16 22:06

    Based on the code that connector published I came up with the following solution that supports different row / column sizes (though I have admittedly not tested that), takes the sizes of the item decorations into account and handles the different measure modes properly.

    Here is the code:

    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);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-16 22:13

    Thanks @nantoka and @arthur for the code, it gave me a good start. Unfortunately, it didn't work well with different spanCount, so i here is my update (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) {
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题