Reputation: 8231
I have a page that consists of an ImageView
, and a RecyclerView
. The RecyclerView
contains a small number of items (currently three) and only takes up around a quarter of the screen on my test device. However, despite trying numerous layout options, I cannot get the RecyclerView
to effectively wrap its content and take up just enough space required to contain these three rows, and leave the rest of the space for my ImageView
.
To help illustrate what I mean, I have drawn two diagrams. The first shows what I would like to happen, and the second what is happening:
So far, I have tried several different combinations of RelativeLayout
- for instance, I will set RecyclerView
to layout:align_ParentBottom
and the second RelativeLayout
that contains the ImageView
to layout:alignComponent
so that its bottom matches the RecyclerView
top (this would normally drag the ImageView
layout so that it fills any reminaing space, which is what I would like to happen.)
The RecyclerView
though just keeps occupying all the space it can, even though it only contains a few rows. The current "solution" I have is to place everything inside a LinearLayout
and set less gravity
to the RecyclerView
, but it isn't ideal, because on different emulators it wont line up completely with the bottom of the screen, and in others there isn't enough room and the RecyclerView
becomes scrollable.
Thanks in advance for any help and suggestions anyone can offer.
Upvotes: 3
Views: 5112
Reputation: 10829
The only solution I can think of is using layout weight. Specify 70% of the screen for the image and 30% for the Recyclerview
as you said you have just 3 rows. Use adjustViewByBounds
to ensure the images aspect ratio is maintained.
My code below:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:src="@drawable/ic_round_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:layout_weight=".9"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight=".1"/>
</LinearLayout>
Upvotes: 0
Reputation: 8231
Many thanks to everyone who contributed, but I have found a programmatic solution outside of the layout
files. In case anyone else is looking for a solution to this problem, I found one here.
It appears as if there is an issue with RecyclerView
currently where it doesn't wrap content. The answer is to construct a custom class that extends LinearLayoutManager
. I have posted the solution that worked for me below - most of it is copy and pasted from the answer given in the link I quoted. The only small issue is that it doesn't account for the extra space added by decorations, which is why I had to make a small tweak to the following line near the end of the code:
//I added the =2 at the end.
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin + 2;
Here is the code in its entirety:
public class HomeLinearLayoutManager extends LinearLayoutManager{
HomeLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
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) {
width = width + mMeasuredDimension[0];
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
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 + 2;
recycler.recycleView(view);
}
}
}
Thanks again.
Upvotes: 3
Reputation: 1
Put the two in a RelativeLayout and make ImageView fill parent:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<RecyclerView
android:id="@+id/recyclerView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"/>
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="@id/recyclerView"
/>
</RelativeLayout>
Edit: Wrote TextView by accident. Fixed.
Upvotes: 0