Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ViewPager2 with differing item heights and WRAP_CONTENT

Tags:

There are a few posts on getting ViewPager to work with varying height items that center around extending ViewPager itself to modify its onMeasure to support this.

However, given that ViewPager2 is marked as a final class, extending it isn't something that we can do.

Does anyone know if there's a way to make this work out?


E.g. let's say I have two views:

View1 = 200dp

View2 = 300dp

When the ViewPager2 (layout_height="wrap_content") loads -- looking at View1, its height will be 200dp.

But when I scroll over to View2, the height is still 200dp; the last 100dp of View2 is cut off.

like image 585
Mephoros Avatar asked Oct 21 '19 19:10

Mephoros


People also ask

How do I set ViewPager height dynamically in Android Kotlin?

Create a custome widget for viewpager class. and in xml use it for viewpager. Then in the view pager adapter override the below method and add the code. Make your view pager height wrap_content inside xml file so you can check the result.

What is setOffscreenPageLimit in Android?

Android ViewPager setOffscreenPageLimit(int limit) Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state.


1 Answers

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

like image 51
Mephoros Avatar answered Nov 09 '22 18:11

Mephoros