Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make last Item / ViewHolder in RecyclerView fill rest of the screen or have a min height

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:

  • Model has a few values, which in sum take up 100dp of a 600dp screen -> map heigh should be 500dp
  • Model has a lot of values, which in sum take up 500dp of a 600dp screen -> map heigh should be a min value of 200dp
  • Model has no values -> map fills whole screen

enter image description here

like image 398
xxtesaxx Avatar asked Dec 21 '16 01:12

xxtesaxx


People also ask

How do you set different height for each item in recycler view?

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.

What is ViewHolder in RecyclerView?

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.

How do I reduce space between items in RecyclerView?

Try to use cardElevation=0dp. This should remove the extra spacing between recyclerview items.


2 Answers

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).

like image 152
muthuraj Avatar answered Sep 19 '22 16:09

muthuraj


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)
        }
    }
}
like image 39
David Medenjak Avatar answered Sep 20 '22 16:09

David Medenjak