Search code examples
androidandroid-recyclerviewlayoutmargins

Remove margin of element only for last element in Recyclerview


I have a recyclerview that may contain elements of two types, with different bottom_margin. let's say type A is having a bottom margin of x dp to the next element, while type B is having a bottom margin of y dp to the next element. This margin does not depend on whether the next element is of type A or of type B. It is just a bottom-margin set in the layout of the itemview. However, the last element of the list should have no bottom margin as it is the end of the list.

I cannot use clipToPadding = false with padding on the recyclerview, as the margin should also apply between elements. I cannot add a ItemDecorator to add the margin as I use ListAdapters with DiffUtils for some lists and only changed items get updated, not the ones around. Rerendering the whole list every time I get an update would be way too inefficient and also a bad workaround.

So my question is: Is there a way to just ignore margin of the last element and draw end of recyclerview before that? I've seen margins getting ignored when missing constraints in ConstraintLayouts, so I thought maybe there was a way to produce that for RecyclerViews in a LinearLayoutManager as well.


Solution

  • I think that you can accomplish what you want with RecyclerView.ItemDecoration but it would depend on whether you can make the decoration look like a margin or not.

    You can also do what you want with DiffUtil which you are already using.

    When you bind the view holders, check to see if the item that is being bound is the last item of the list. If it is, remove (or reduce) the margin; if it is not, set the margin appropriately for the type of element.

    When the contents of the RecyclerView changes, you will need to be concerned about the last item of the old list and the last item of the new list. There are a few states to consider:

    1. If the two last items are the same.
    2. The last item of the old list exists in the new list and is attached to a view holder and/or the last item of the new list is attached to a view holder.

    In case #1, there is nothing to do since the last item hasn't changed.

    In case #2, you will need to change the margins of the view holder(s). (Of course, if neither item is bound, there will be nothing to do.)

    Use the abstract methods of DiffUtil areContentsTheSame() and areItemsTheSame() to check the above conditions. getChangePayload() can be used to structure a change to the margins. As the documentation states:

    When areItemsTheSame returns true for two items and areContentsTheSame returns false for them, DiffUtil calls this method to get a payload about the change.

    Use this binding method to structure a payload that identify how a margin in a view holder will be changed. The idea here is that there is a "lastness" characteristic of the data that has changed.

    Once the margin change is identified and the payload structured, a version of onBindViewHolder() is called with the payload. Use this method to change the margin in the view holder. See RecyclerView.Adapter#onBindViewHolder().

    I am sure that there are details to be worked out, but that is the gist of the approach.