0

I have a ViewPager2 inside a BottomSheetDialog in which I load a Fragment that contains a ComposeView. Inside this view I populate a LazyList with items as soon as they're loaded. Now this works all fine, except that the ViewPager2 makes no height adaptions when it's inner contents change, so naturally I adapted the peekHeight at first and then added a GlobalLayoutListener to give the pager the height of the inner, currently displayed fragment view, like so:

val myPager = ...
myPager.registerOnPageChangeCallback(AdaptChildHeightOnPageChange(myPager))
...
internal class AdaptChildHeightOnPageChange(private val viewPager: ViewPager2) : ViewPager2.OnPageChangeCallback() {
    private val otherViews = mutableSetOf<View>()

    private fun getViewAtPosition(position: Int): View =
        (viewPager.getChildAt(0) as RecyclerView).layoutManager?.findViewByPosition(position)
            ?: error("No layout manager set or no view found at position $position")

    override fun onPageSelected(position: Int) {
        super.onPageSelected(position)
        val itemView = getViewAtPosition(position)
        val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
            itemView.updatePagerHeightForChild()
        }
        // remove the global layout listener from other views
        otherViews.forEach { it.viewTreeObserver.removeOnGlobalLayoutListener(it.tag as ViewTreeObserver.OnGlobalLayoutListener) }
        itemView.viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
        itemView.tag = layoutListener
        otherViews.add(itemView)
    }

    private fun View.updatePagerHeightForChild() {
        post {
            val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY)
            val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            measure(wMeasureSpec, hMeasureSpec)

            if (viewPager.layoutParams.height != measuredHeight) {
                viewPager.layoutParams = (viewPager.layoutParams as ViewGroup.LayoutParams)
                    .also { lp -> lp.height = measuredHeight }
            }
        }
    }
}

(taken and adapted from https://stackoverflow.com/a/58632613/305532)

Now while this works fantastically with regular compose content, as soon as I switch my compose view to the LazyList implementation (or anything that uses Modifier.verticalScroll(...)), I receive the following exception:

Nesting scrollable in the same direction layouts like LazyColumn and \
Column(Modifier.verticalScroll()) is not allowed (Scroll.kt:370) 

But I don't get this really, because I haven't nested any vertical-scolling compose elements that could trigger this exception. My only guess is that because of the height constraint I give to the ViewPager2 this internally triggers the enablement of vertical scrolling, making the inner LazyList unable to take over.

How can I solve this issue?

Thomas Keller
  • 5,933
  • 6
  • 48
  • 80

1 Answers1

0

Ok, the crash seem to have stem from an issue with the GlobalLayoutListener. This constantly fired updates and kicked of relayouts, even though I tried to remove the listener explicitely before setting a new height to the surrounding pager.

Thomas Keller
  • 5,933
  • 6
  • 48
  • 80