ViewPager2 with differing item heights and WRAP_CONTENT
Just do this for the desired Fragment
in ViewPager2
:
override fun onResume() {
super.onResume()
layoutTaskMenu.requestLayout()
}
Jetpack: binding.root.requestLayout()
(thanks @syed-zeeshan for the specifics)
The solution is to register a PageChangeCallback
and adjust the LayoutParams
of the ViewPager2
after asking the child to re-measure itself.
pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
val view = // ... get the view
view.post {
val wMeasureSpec = MeasureSpec.makeMeasureSpec(view.width, MeasureSpec.EXACTLY)
val hMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
view.measure(wMeasureSpec, hMeasureSpec)
if (pager.layoutParams.height != view.measuredHeight) {
// ParentViewGroup is, for example, LinearLayout
// ... or whatever the parent of the ViewPager2 is
pager.layoutParams = (pager.layoutParams as ParentViewGroup.LayoutParams)
.also { lp -> lp.height = view.measuredHeight }
}
}
}
})
Alternatively, if your view's height can change at some point due to e.g. asynchronous data load, then use a global layout listener instead:
pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
private val listener = ViewTreeObserver.OnGlobalLayoutListener {
val view = // ... get the view
updatePagerHeightForChild(view)
}
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
val view = // ... get the view
// ... IMPORTANT: remove the global layout listener from other views
otherViews.forEach { it.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener) }
view.viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
}
private fun updatePagerHeightForChild(view: View) {
view.post {
val wMeasureSpec = MeasureSpec.makeMeasureSpec(view.width, MeasureSpec.EXACTLY)
val hMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
view.measure(wMeasureSpec, hMeasureSpec)
if (pager.layoutParams.height != view.measuredHeight) {
// ParentViewGroup is, for example, LinearLayout
// ... or whatever the parent of the ViewPager2 is
pager.layoutParams = (pager.layoutParams as ParentViewGroup.LayoutParams)
.also { lp -> lp.height = view.measuredHeight }
}
}
}
}
See discussion here:
https://issuetracker.google.com/u/0/issues/143095219
In my case, adding adapter.notifyDataSetChanged()
in onPageSelected
helped.