Reputation: 14562
I currently have a need to use a RecyclerView (or ListView) but the number of items is fixed at 4. I want those 4 items to equally use the available space on the screen. The RecyclerView is the only view on the screen except for the app bar. I.e. RecyclerView has layout_height
set to match_parent
. I chose the RecyclerView because the items have different layouts depending on model state.
I haven't looked it up yet, but I'm sure I can set the height programmatically in Java code for each item. But that seems inelegant if I can specify it in the XML. Similar questions to my right, as I write this, answer that question.
I've tried the following in the item_layout.xml file, the way layout_weight
is used in other scenarios:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="0dp">...</LinearLayout>
But then nothing shows on screen.
What I'd like to know is, can I do this in the layout XML with layout_weight
, or something else? If so, how?
Upvotes: 19
Views: 17020
Reputation: 1
I have a similar issue just to keep items in horizontal recycleView spread equally which means each of them has an equal space, here is my CircleItemDecoration (in Kotlin
):
class CircleItemDecoration() : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
Timber.d("parent.width = ${parent.width} outRect = ${view.width}")
val itemCount = parent.adapter?.itemCount ?: 0
if (itemCount <= 1) return
if (parent.getChildAdapterPosition(view) != 0) {
outRect.left = (parent.width - view.width * itemCount) / (itemCount - 1)
}
}
}
and one trick for this is we need to "addItemDecoration" in doOnPreDraw, which ensure the measure of each UI element has been calculated:
rvSpeedControl.doOnPreDraw {
rvSpeedControl.addItemDecoration(CircleItemDecoration())
}
Upvotes: 0
Reputation: 2260
It could be simply accomplished using a custom ItemDecoration:
class SameLinearSpaceItemDecoration(
private val minSpanCount: Int = 1
, private val paddingHorizontal: Int = 0
, private val paddingVertical: Int = 0
) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
if (parent.layoutManager !is LinearLayoutManager) return
val spanCount = (Math.max(minSpanCount, parent.adapter?.itemCount ?: 1))
if ((parent.layoutManager as LinearLayoutManager).orientation == RecyclerView.HORIZONTAL) {
view.layoutParams.width = (parent.measuredWidth - paddingHorizontal - paddingHorizontal) / spanCount
} else { // vertical
view.layoutParams.height = (parent.measuredHeight - paddingVertical - paddingVertical) / spanCount
}
view.setPadding(if (paddingHorizontal == 0) view.paddingLeft else paddingHorizontal
, if (paddingVertical == 0) view.paddingTop else paddingVertical
, if (paddingHorizontal == 0) view.paddingRight else paddingHorizontal
, if (paddingVertical == 0) view.paddingBottom else paddingVertical)
}
}
Upvotes: 1
Reputation: 1
By ItemDecoration:
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int itemWidth = parent.getMeasuredWidth() / 7;
view.getLayoutParams().width = itemWidth;
}
Upvotes: 0
Reputation: 421
Try this, work for me
int weight = 3; //number of parts in the recycler view.
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View rowView = LayoutInflater.from(parent.getContext()).inflate(R.layout.row,parent,false);
rowView.getLayoutParams().height = parent.getHeight() / weight;
return new ViewHolder(rowView);
}
Upvotes: 12
Reputation: 2266
I ran into this issue, as I had a RecyclerView with variable entries. My horizontal RecyclerView needed to fit at least three views without overflowing, so I constructed my views to be resized at runtime to fit the screen.
class MyRecyclerView extends RecyclerView.Adapter<ViewHolder> {
int mViewWidth;
public MyRecyclerView(Context context) {
mViewWidth = defineViewWidth(context);
}
private int defineViewWidth(Context context) {
int definedWidth = (int) context.getResources().getDimension(R.dimen.default_view_width);
DisplayMetrics dm = new DisplayMetrics();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(dm);
if (definedWidth * 3 > dm.widthPixels) {
return dm.widthPixels / 3;
}
return definedWidth;
}
...
}
Once you have a width defined, you need to set the view width:
@Override
public ViewHolder onCreateViewHolder(ViewHolder holder, int position) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.row, parent, false);
ViewGroup.LayoutParams params = v.getLayoutParams();
params.width = mViewWidth;
v.setLayoutParams(params);
return new ViewHolder(v);
}
You could easily modify this to make sure your RecyclerView fits as many elements (vertically or horizontally) as you need.
Upvotes: 2
Reputation: 3908
As the API reference says, layout_weight
is an attribute of LinearLayout
, so it cannot be used with other ViewGroups like, for instance, RecyclerView
and ListView
. Instead, you have to set the item's height programmatically.
Since you seem worried about your code's cleanliness, I think this is a rather elegant solution in case you use a RecyclerView
:
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater
.from(parent.getContext())
.inflate(R.layout.item_list, null);
int height = parent.getMeasuredHeight() / 4;
int width = parent.getMeasuredWidth();
view.setLayoutParams(new RecyclerView.LayoutParams(width, height));
return new ViewHolder(view);
}
Upvotes: 32