I'm struggeling with the RecyclerView. I use a recycler view to display the details of my model class.
//My model class
MyModel {
String name;
Double latitude;
Double longitude;
Boolean isOnline;
...
}
Since some of the values might not be present, I use the RecyclerView with custom view types (one representing each value of my model).
//Inside my custom adapter
public void setModel(T model) {
//Reset values
itemCount = 0;
deviceOfflineViewPosition = -1;
mapViewPosition = -1;
//If device is offline, add device offline item
if (device.isOnline() == null || !device.isOnline()) {
deviceOfflineViewPosition = itemCount;
itemCount++;
}
//Add additional items if necessary
...
//Always add the map as the last item
mapViewPosition = itemCount;
itemCount++;
notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
if (position == deviceOfflineViewPosition) {
return ITEM_VIEW_TYPE_OFFLINE;
} else if (position == mapViewPosition) {
return ITEM_VIEW_TYPE_MAP;
} else if (...) {
//Check for other view types
}
}
With the RecyclerView I can easily determine at runtime which values are available and add corresponding items to the RecyclerView datasource. I simplyfied the code but my model has a lot more values and I have a lot more view types.
The last item in the RecyclerView is always a map and it is always present. Even if there is no value at all in my model, there will at least be one item, the map.
PROBLEM: How can I make the last item in RecyclerView fill the remaining space on screen and also have a min heigh. The size shall be what ever value is lager: the remaining space or the min height. For example:
solution: In the item layout xml, use RelativeLayout as root only and ensure the layout whose child view be dynamically added is surrounded with RelativeLayout too.
A ViewHolder describes an item view and metadata about its place within the RecyclerView. Adapter implementations should subclass ViewHolder and add fields for caching potentially expensive findViewById results. While LayoutParams belong to the LayoutManager , ViewHolders belong to the adapter.
Try to use cardElevation=0dp. This should remove the extra spacing between recyclerview items.
You can find the remaining space in RecyclerView after laying out the last item and add that remaining space to the minHeight
of the last item.
val isLastItem = getItemCount() - 1 == position
if (isLastItem) {
val lastItemView = holder.itemView
lastItemView.doOnLayout {
val recyclerViewHeight = recyclerView.height
val lastItemBottom = lastItemView.bottom
val heightDifference = recyclerViewHeight - lastItemBottom
if (heightDifference > 0) {
lastItemView.minimumHeight = lastItemView.height + heightDifference
}
}
}
In onBindHolder
check if the item is last item using getItemCount() - 1 == position
. If it is the last item, find the height difference by subtracting recyclerView height with lastItem bottom (getBottom()
gives you the bottom most pixel of the view relative to it's parent. In this case, our parent is RecyclerView
).
If the difference is greater than 0, then add that to the current height of the last view and set it as minHeight
. We are setting this as minHeight
instead of setting directly as height
to support dynamic content change for the last view.
Note: This code is Kotlin, and doOnLayout function is from Android KTx. Also your RecyclerView
height should be match_parent
for this to work (I guess that's obvious).
You can extend LinearLayoutManager
to layout the last item yourself.
This is a FooterLinearLayoutManager
that will move the last item of the list to the bottom of the screen (if it isn't already there). By overriding layoutDecoratedWithMargins
the LinearLayouyManager
calls us with where the item should go, but we can match this against the parent height.
Note: This will not "resize" the view, so fancy backgrounds or similar probably won't work, it will just move the last item to the bottom of the screen.
/**
* Moves the last list item to the bottom of the screen.
*/
class FooterLinearLayoutManager(context: Context) : LinearLayoutManager(context) {
override fun layoutDecoratedWithMargins(child: View, left: Int, top: Int, right: Int, bottom: Int) {
val lp = child.layoutParams as RecyclerView.LayoutParams
if (lp.viewAdapterPosition < itemCount - 1)
return super.layoutDecoratedWithMargins(child, left, top, right, bottom)
val parentBottom = height - paddingBottom
return if (bottom < parentBottom) {
val offset = parentBottom - bottom
super.layoutDecoratedWithMargins(child, left, top + offset, right, bottom + offset)
} else {
super.layoutDecoratedWithMargins(child, left, top, right, bottom)
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With