After I made a RecyclerView grid with a header, I realized that I want to make it auto fit as well. I want to define the size of each item and let the system compute the spanCount
automatically.
To determine the number of spans, I need two things: the width of the RecyclerView
, and the width of each item.
AutofitRecyclerView
We extend RecyclerView
to get access to its width. As for the width of each item,
when GridView
has android:numColumns="auto_fit"
, it uses android:columnWidth
to compute the number of columns. Let's reuse that attribute.
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 GridLayoutManager(getContext(), 1); setLayoutManager(manager); }
In the constructor, we read the value of android:columnWidth
and save it in a member variable.
Later in onMeasure
will will use it to determine the span count.
Even though we will be setting the span count in onMeasure
, the app will crash if we wait until then to define a GridLayoutManager
, so we create one here as well, with a span count of 1.
In onMeasure
, we ask the super class to perform the measurement, then take the value from getMeasuredWidth
to compute the span count.
protected void onMeasure(int widthSpec, int heightSpec) { super.onMeasure(widthSpec, heightSpec); if (columnWidth > 0) { int spanCount = Math.max(1, getMeasuredWidth() / columnWidth); manager.setSpanCount(spanCount); } }
Notice the Math.max
call? This makes sure that we will have at least a span count of 1, even if the column width is defined to be larger than the width of the RecyclerView
.
With that, the span count changes with the width of the RecyclerView
.
One thing to note: we are using the column width the compute the span count, and once that is given to the RecyclerView
it will turn around and compute the width of each item. So for instance if your RecyclerView
is 320dp
wide and you use a column width of 72dp
, a span count of 4
(320dp / 72dp = 4.4444
) will be given to the RecyclerView
, which will make your items 320dp / 4 = 80dp
wide, not 72dp
. Make sure your item layout takes that into account.
Source: https://github.com/chiuki/android-recyclerview
Why bother?
Since GridView
already supports auto fit, why bother reimplementing it using RecyclerView
? RecyclerView
has a lot of other functionalities like built-in animations for inserting and removing items, reordering etc, so you may want to use that instead of GridView
.
Inline coding questions will not be answsered. Instead, ask on StackOverflow and put the link in the comment.
Great article! I've been looking at implementing a custom gridview to set consistent row heights, but now I'm thinking it might be time to jump ship to the RecyclerView. Thanks for sharing.
ReplyDeleteWorks perfectly!
ReplyDeleteThank you so much. Awesome tricky work!
ReplyDeleteThanks for great tutorial!
ReplyDeleteThank you so much. Awesome tricky work!
ReplyDeleteThank you. A very efficent way to solve an important problem.
ReplyDeleteThanks a lot...
ReplyDeleteShow! Awesome work
ReplyDeleteDoes your AutoFitRecyclerView work well when being used with a LinearLayoutManager rather than a GridLayoutManager? I’m currently using it with a LinearLayoutManager and there aren’t any issues. But I’m now wondering if are there any performance issues that I may be over looking.
ReplyDeleteGreat block post by the way!
I'm surprised that you are using AutoFitRecyclerView with LinearLayoutManager because it doesn't have span count so the code shouldn't compile.
Delete