doOnPreDraw method not getting called for some items in a recyclerView
Asked Answered
Y

1

7

I'm having troubles with some animation in a recycler view. I do the relevant measurements in onViewAttachedToWindow:

override fun onViewAttachedToWindow(holder: PairingViewHolder) {
        super.onViewAttachedToWindow(holder)

        // get originalHeight & expandedHeight if not gotten before
        if (holder.expandedHeight < 0) {
            // Execute pending bindings, otherwise the measurement will be wrong.
            holder.itemViewDataBinding.executePendingBindings()
            holder.cardContainer.layoutParams.width = expandedWidth
            holder.expandedHeight = 0 // so that this block is only called once

            holder.cardContainer.doOnLayout { view ->

                holder.originalHeight = view.height
                holder.expandView.isVisible = true

                // show expandView and record expandedHeight in next layout pass
                // (doOnPreDraw) and hide it immediately.
                view.doOnPreDraw {
                    holder.expandedHeight = view.height
                    holder.expandView.isVisible = false
                    holder.cardContainer.layoutParams.width = originalWidth
                }
            }
        }
    }

The problem is that doOnPreDraw gets called just for some views. It is something related to the visibility of the views I guess, since the smaller the items (expanded) are, the highest the count of the ones on which onPreDraw gets called.

My guess is that since I'm expanding them in onLayout, the recyclerView consider visible only the ones that when expanded are actually visible on screen. In onPreDraw I collapse them, resulting in some views being able to animate correctly and some not.

How would you solve this?

Thanks in advance.

Ytterbia answered 16/10, 2020 at 15:18 Comment(2)
Do you find any improvment ? i am stuck on the same problemBoddie
Hi, I actually don't recall if I solved it or not because eventually I ended up creating a custom class for that kind of view which handles the resizing itself, so the recyclerview now just calls the corresponding method to expand/collapse the view.Ytterbia
C
0

I had the same issue, and got able to figure it out.

What happened: Only the first item that is not initialy visible on the recycler view is really affected for me, all others were behaving as expected.

What was causing it: doOnPreDraw uses OneShotPreDrawListener, and it only detaches if either onPreDraw is called or onViewDetachedFromWindow is called.
Here is the documentation for doOnDetach as a reference.

Performs the given action when this view is detached from a window. If the view is not attached to a window the action will be performed immediately, otherwise the action will be performed after the view is detached from its current window.

On a recycler view we are creating the view detached from a window initally, which means that onViewDetachedFromWindow will be called imediatelly.
For the already visible views that is not an issue as onPreDraw was also being called imediatally.
But for the views that are not yet visible, onPreDraw listener would just be removed before it could ever run.

Disclaimer: I've tried multiple simpler different solutions, but I've always ended up caught on some edge case, so the only solution that seems to work properly is as follows. (But I would be glad if anyone find a simpler one).

Solution: I've copied the entire OneShotPreDrawListener and removed removeListener from onViewDetachedFromWindow method.
Saddly I could not extend as the class is final.

Craft answered 15/8, 2023 at 16:15 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.