Search code examples
androidandroid-recyclerviewandroid-cardviewandroid-elevation

Recycler view item decoration and view elevation


I use recycler view decoration to add margin before specific items.

My items - mostly MaterialCardView - have an important elevation : 16dp (thanks to our UI guys...)

Elevations are allowed to be drawn outside item view container with

android:clipChildren="false"
android:clipToPadding="false"

My problem is : elevation is truncated by recycler view decoration.

here decoration code :

class TopSpacingItemDecoration(context: Context) : RecyclerView.ItemDecoration() {

    private val spacing = context.resources.getDimension(R.dimen.margin_before_section)

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        val position = parent.getChildAdapterPosition(view)
        val type = adapter.getItemViewType(position)

        if (type == adapter.SPECIFIC_TYPE || other conditions) {
            outRect.set(0, spacing.toInt(), 0, 0)
        }
    }
}

It looks like :

exemple

FYI : I don't want to manage margins on item because recycler view order is dynamic

Any idea how fix this ? 🙏🙏🙏


Solution

  • You're using ItemDecorations to do something they are not designed to do, yet you don't want to use the right thing and now you want to hack a solution? (Did I get this right?)

    In your quest to solve this riddle, I'll give you my 0.02c:

    1. ItemDecorations are not designed to do what you want them to do, hence why you see these side-effects.
    2. ItemDecorations are RecyclerView's way of giving you, a chance to "decorate" a view, by having a canvas and a few milliseconds to paint something on the screen, completely detached of the View/Layout/Measure engine.
    3. You specifically ban the usage of item margins (and padding?) because of "dynamic order". I'm not entirely sure what order has to do with this, but I cannot fight you on that front; what order? item order? what does the order have to do with margins or spacing? (I'm sure there are reasons, you didn't list them). How would order affect this? Are the margins/spacing uneven on different sides because items are different? I infer this, because you provide a small hint in that if (item == type_xxx) {} so I assume you're using some sort of itemType to render different views in your recycler view, and now you want to add some dynamic margins to these items, based upon their neighbors.

    I am assuming all this.

    Answer: you cannot draw outside the boundaries of the view from an ItemDecorator because as cool as it is, the Canvas passed onto it (so it can draw), is already calculated and constrained by the Android measure/layout engine.

    Also:

    android:clipChildren="false"
    android:clipToPadding="false"
    

    has side-effects (performance wise, this is not great at all).

    Performance aside, you simply cannot (or rather, should not!) try to do it this way. The Decorations are not meant to be a "free for all draw wherever you want" card. I suggest you provide proper padding/margin where this belongs, in the item views and/or recyclerview. They can deal with density pixels as well as account for build variants and flavors when provided via resources (dimens.xml for example).

    None of these technologies would be available to an Item Decorator. But if you also ignore the code separation, single responsibility principle, inversion of control, and all this jargon thingy, you will still be unable do perform that, because your canvas is precalculated for you, and so are your dreams of drawing anywhere on the screen.

    (ok, you can draw anywhere, but that doesn't PUSH other views, you're simply drawing on top of the content, not touching it).