Skip to content

Instantly share code, notes, and snippets.

@ArthurSav
Created September 11, 2015 14:45
Show Gist options
  • Star 29 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save ArthurSav/5f80e19d9ba6d562fbd5 to your computer and use it in GitHub Desktop.
Save ArthurSav/5f80e19d9ba6d562fbd5 to your computer and use it in GitHub Desktop.
GridLayoutManager with working wrap_content.
package com.inperson.android.utils.leastview;
import android.content.Context;
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[] mMeasuredDimension = 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 width = 0;
int height = 0;
for (int i = 0; i < getItemCount(); i++) {
measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
if (getOrientation() == HORIZONTAL) {
if (i % getSpanCount() == 0) {
width = width + mMeasuredDimension[0];
}
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
if (i % getSpanCount() == 0) {
height = height + mMeasuredDimension[1];
}
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) {
View view = recycler.getViewForPosition(position);
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;
recycler.recycleView(view);
}
}
}
@riyazMuhammad
Copy link

Good one buddy.

I tried it with the items being a textview and compound drawables on it. So I was seeing a little scroll and then top most and bottom most rows were being cut a little. so did the following

  1. setMeasuredDimension(width, height+10);
@Override
public boolean canScrollVertically() {
   return false;
}

And now it's all fine. Hope it helps someone.

@NileshJarad
Copy link

I used this to Wrap the Grid of RecyclerView Working fine

mMultiSelectView.setLayoutManager(new WrappableGridLayoutManager(mContext, 2));

But problem is when I used SpaceItemDecoration its cutting the item
mMultiSelectView.addItemDecoration(new SpaceItemDecoration(20, LinearLayoutManager.VERTICAL));

Help me with this, Thanks.

@gzncland
Copy link

Great Thanks

@zeinabmohamed
Copy link

@riyazMuhammad please could u describe where exctly set

  1. setMeasuredDimension(width, height+10);
    as i set in onMesure but no effect

@ashton719
Copy link

if child layout has that Custom Square Image View

List size = measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(widthSize / getSpanCount(), widthMode),
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED));

Copy link

ghost commented Dec 28, 2015

Hi
What is the license of your code ?

@kyonghwa
Copy link

Works perfectly
Thanks

@harshbangari
Copy link

//Method threw 'java.lang.IndexOutOfBoundsException' :Invalid item position 0(0). Item count:0 in measureScrapChild() at line:
View view = recycler.getViewForPosition(position);

@ccpy
Copy link

ccpy commented Apr 28, 2016

//Method threw 'java.lang.IndexOutOfBoundsException' :Invalid item position 0(0). Item count:0 in measureScrapChild() at line:
View view = recycler.getViewForPosition(position);

@geolyth
Copy link

geolyth commented Apr 28, 2016

With Android M got this:

//Method threw 'java.lang.IndexOutOfBoundsException' :Invalid item position 0(0). Item count:0 in measureScrapChild() at line:
View view = recycler.getViewForPosition(position);

@tcoolpie
Copy link

//Method threw 'java.lang.IndexOutOfBoundsException' :Invalid item position 0(0). Item count:0 in measureScrapChild() at line:
View view = recycler.getViewForPosition(position);

@Dmitry-Borodin
Copy link

to fix

:Invalid item position 0(0). Item count:0 in measureScrapChild()

change View view = recycler.getViewForPosition(position); to this code:

View view = null;
        try {
            view = recycler.getViewForPosition(position);
        } catch (Exception ex) {
          // try - catch is needed since support library version 24
        }

@sough
Copy link

sough commented Jan 5, 2017

Hello, thx for this snippet, but i have a problem - recycler view binding ALL his views on initialisation and again on scroll

@nikolamin
Copy link

Kotlin version with support for different spanCount:


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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment