views-widgets-samples
views-widgets-samples copied to clipboard
How to wrap height of Android ViewPager2 to height of current item?
The content of each piece of mine is long and short. How can I make viewpager2 fit the height of the subview?
This is the layout:
<?xml
version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/nestedScrollView" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/grey_300">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:ignore="HardcodedText">
<com.google.android.material.tabs.TabLayout
android:id="@+id/mTablayout"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_gravity="start"
android:textSize="15sp"
app:layout_constraintTop_toTopOf="parent"
app:tabBackground="@color/white"
app:tabIndicatorColor="@color/light_blue_400"
app:tabIndicatorHeight="50dp"
app:tabMode="auto"
app:tabSelectedTextColor="@color/white"
app:tabTextColor="@color/black_alpha_176" />
<View android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/grey_500"
app:layout_constraintTop_toBottomOf="@+id/mTablayout" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@+id/divider" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView> `
The following is the method I used before, but it doesn't work
`mViewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { super.onPageSelected(position); /* View childView = mViewPager.getChildAt(position); View rootView = mViewPager.getRootView();*/
Fragment fragment = mAdapter.createFragment(position);
View childView = fragment.getView();
if (childView == null) return;
int wMeasureSpec = View.MeasureSpec.makeMeasureSpec(childView.getWidth(), View.MeasureSpec.EXACTLY);
int hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
childView.measure(wMeasureSpec, hMeasureSpec);
if (mViewPager.getLayoutParams().height != childView.getMeasuredHeight()) {
ViewGroup.LayoutParams lp = mViewPager.getLayoutParams();
lp.height = childView.getMeasuredHeight();
}
}
});`
Why can't viewpager2 be inherited without openness at all
dataBinding.viewPager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageScrolled( position: Int, positionOffset: Float, positionOffsetPixels: Int ) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) if (position > 0 && positionOffset == 0.0f && positionOffsetPixels == 0) { dataBinding.viewPager2.layoutParams.height = dataBinding.viewPager2.getChildAt(0).height } } }). try it
any luck for solving the issue
@hadia could you paste the code of solving.
@sindicly 兄弟,你这个问题解决了没?我也遇到了,用了你上面说的方法,网上也都说的这个方法。但是存在问题。
I've implemented the viewPager2.registerOnPageChangeCallback and added listener in viewpager2's fragment, after data loaded completed and UI render completed, ask the surveyViewPager to reset height again
It solve the problem
I've implemented the viewPager2.registerOnPageChangeCallback and added listener in viewpager2's fragment, after data loaded completed and UI render completed, ask the surveyViewPager to reset height again
It solve the problem if u mind, show me some light plexx ...
this is for wrapping the height of each view
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
class ViewPager2ViewHeightAnimator {
var viewPager2: ViewPager2? = null; set(value) {
if (field != value) {
field?.unregisterOnPageChangeCallback(onPageChangeCallback)
field = value
value?.registerOnPageChangeCallback(onPageChangeCallback)
}
}
private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager
private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback(){
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
recalculate(position, positionOffset)
}
}
fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply {
val leftView = findViewByPosition(position) ?: return@apply
val rightView = findViewByPosition(position + 1)
viewPager2?.apply {
val leftHeight = getMeasuredViewHeightFor(leftView)
layoutParams = layoutParams.apply {
height = if (rightView != null) {
val rightHeight = getMeasuredViewHeightFor(rightView)
leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt()
} else {
leftHeight
}
}
invalidate()
}
}
private fun getMeasuredViewHeightFor(view: View) : Int {
val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY)
val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
view.measure(wMeasureSpec, hMeasureSpec)
return view.measuredHeight
}
}
thanks dude)
this is for wrapping the height of each view
import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 class ViewPager2ViewHeightAnimator { var viewPager2: ViewPager2? = null; set(value) { if (field != value) { field?.unregisterOnPageChangeCallback(onPageChangeCallback) field = value value?.registerOnPageChangeCallback(onPageChangeCallback) } } private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback(){ override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) recalculate(position, positionOffset) } } fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply { val leftView = findViewByPosition(position) ?: return@apply val rightView = findViewByPosition(position + 1) viewPager2?.apply { val leftHeight = getMeasuredViewHeightFor(leftView) layoutParams = layoutParams.apply { height = if (rightView != null) { val rightHeight = getMeasuredViewHeightFor(rightView) leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt() } else { leftHeight } } invalidate() } } private fun getMeasuredViewHeightFor(view: View) : Int { val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY) val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) view.measure(wMeasureSpec, hMeasureSpec) return view.measuredHeight } }
I do some improvements on it to change the height of viewpager if view height changes on runtime:
class ViewPager2ViewHeightAnimator {
var viewPager2: ViewPager2? = null; set(value) {
if (field != value) {
field?.unregisterOnPageChangeCallback(onPageChangeCallback)
field = value
value?.registerOnPageChangeCallback(onPageChangeCallback)
}
}
private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager
private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
recalculate(position, positionOffset)
}
}
fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply {
val leftView = findViewByPosition(position) ?: return@apply
val rightView = findViewByPosition(position + 1)
val setMeasure = {
viewPager2?.apply {
val leftHeight = getMeasuredViewHeightFor(leftView)
layoutParams = layoutParams.apply {
height = if (rightView != null) {
val rightHeight = getMeasuredViewHeightFor(rightView)
leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt()
} else {
leftHeight
}
}
invalidate()
}
}
val onLayoutChanged =
ViewTreeObserver.OnGlobalLayoutListener {
setMeasure.invoke()
}
leftView.viewTreeObserver.addOnGlobalLayoutListener(onLayoutChanged)
rightView?.viewTreeObserver?.addOnGlobalLayoutListener(onLayoutChanged)
setMeasure.invoke()
}
private fun getMeasuredViewHeightFor(view: View): Int {
val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY)
val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
view.measure(wMeasureSpec, hMeasureSpec)
return view.measuredHeight
}
}
how do i implement ViewPager2ViewHeightAnimator ?
this is for wrapping the height of each view
import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 class ViewPager2ViewHeightAnimator { var viewPager2: ViewPager2? = null; set(value) { if (field != value) { field?.unregisterOnPageChangeCallback(onPageChangeCallback) field = value value?.registerOnPageChangeCallback(onPageChangeCallback) } } private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback(){ override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) recalculate(position, positionOffset) } } fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply { val leftView = findViewByPosition(position) ?: return@apply val rightView = findViewByPosition(position + 1) viewPager2?.apply { val leftHeight = getMeasuredViewHeightFor(leftView) layoutParams = layoutParams.apply { height = if (rightView != null) { val rightHeight = getMeasuredViewHeightFor(rightView) leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt() } else { leftHeight } } invalidate() } } private fun getMeasuredViewHeightFor(view: View) : Int { val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY) val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) view.measure(wMeasureSpec, hMeasureSpec) return view.measuredHeight } }
I do some improvements on it to change the height of viewpager if view height changes on runtime:
class ViewPager2ViewHeightAnimator { var viewPager2: ViewPager2? = null; set(value) { if (field != value) { field?.unregisterOnPageChangeCallback(onPageChangeCallback) field = value value?.registerOnPageChangeCallback(onPageChangeCallback) } } private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() { override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) recalculate(position, positionOffset) } } fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply { val leftView = findViewByPosition(position) ?: return@apply val rightView = findViewByPosition(position + 1) val setMeasure = { viewPager2?.apply { val leftHeight = getMeasuredViewHeightFor(leftView) layoutParams = layoutParams.apply { height = if (rightView != null) { val rightHeight = getMeasuredViewHeightFor(rightView) leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt() } else { leftHeight } } invalidate() } } val onLayoutChanged = ViewTreeObserver.OnGlobalLayoutListener { setMeasure.invoke() } leftView.viewTreeObserver.addOnGlobalLayoutListener(onLayoutChanged) rightView?.viewTreeObserver?.addOnGlobalLayoutListener(onLayoutChanged) setMeasure.invoke() } private fun getMeasuredViewHeightFor(view: View): Int { val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY) val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) view.measure(wMeasureSpec, hMeasureSpec) return view.measuredHeight } }
How do i Implement this?
how do i implement ViewPager2ViewHeightAnimator ?
1 Copy this class to your project. 2 Get an instance of it. 3 Pass your viewpager to viewpager2 variable.
Hey what all that I give you guys? To be honest I kind of did it on accident apparently somebody in my house is doing something online and I was trying to figure it out but I really don't know what I'm doing thanks guys
On Fri, Jun 11, 2021, 7:55 AM whatiamdoing @.***> wrote:
thanks dude)
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/android/views-widgets-samples/issues/184#issuecomment-859561668, or unsubscribe https://github.com/notifications/unsubscribe-auth/ATYLZK6TL2GNJ255NBWP4ODTSIBU7ANCNFSM4RA4QZGQ .
Thank you, man I totally lost it there for a sec. Cool thanks
On Wed, Jun 23, 2021, 3:08 PM Jason Rapa @.***> wrote:
Hey what all that I give you guys? To be honest I kind of did it on accident apparently somebody in my house is doing something online and I was trying to figure it out but I really don't know what I'm doing thanks guys
On Fri, Jun 11, 2021, 7:55 AM whatiamdoing @.***> wrote:
thanks dude)
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/android/views-widgets-samples/issues/184#issuecomment-859561668, or unsubscribe https://github.com/notifications/unsubscribe-auth/ATYLZK6TL2GNJ255NBWP4ODTSIBU7ANCNFSM4RA4QZGQ .
how do i implement ViewPager2ViewHeightAnimator ?
1 Copy this class to your project. 2 Get an instance of it. 3 Pass your viewpager to viewpager2 variable.
Get an instance? how
how do i implement ViewPager2ViewHeightAnimator ?
1 Copy this class to your project. 2 Get an instance of it. 3 Pass your viewpager to viewpager2 variable.
Get an instance? how
Do you know programming at all?
how do i implement ViewPager2ViewHeightAnimator ?
1 Copy this class to your project. 2 Get an instance of it. 3 Pass your viewpager to viewpager2 variable.
Get an instance? how
Do you know programming at all?
Noo, please can i see the code
not sure where y'all get your instances, I get mine from the god
On Fri, Jun 25, 2021 at 2:58 AM Ikrimah1998 @.***> wrote:
how do i implement ViewPager2ViewHeightAnimator ?
1 Copy this class to your project. 2 Get an instance of it. 3 Pass your viewpager to viewpager2 variable.
Get an instance? how
Do you know programming at all?
Noo, please can i see the code
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/android/views-widgets-samples/issues/184#issuecomment-867704963, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAGNA44MOQIHZ32262H2NCTTUNBZVANCNFSM4RA4QZGQ .
--
yours Derek
how do i implement ViewPager2ViewHeightAnimator ?
1 Copy this class to your project. 2 Get an instance of it. 3 Pass your viewpager to viewpager2 variable.
Get an instance? how
Do you know programming at all?
Noo, please can i see the code
use findViewById() get your ViewPager2 instance
val viewPager2 = findViewById(R.id.viewpager2)
val viewPager2ViewHeightAnimator = ViewPager2ViewHeightAnimator()
viewPager2ViewHeightAnimator.viewPager2 = viewPager2
how do i implement ViewPager2ViewHeightAnimator ?
1 Copy this class to your project. 2 Get an instance of it. 3 Pass your viewpager to viewpager2 variable.
Get an instance? how
Do you know programming at all?
Noo, please can i see the code
use findViewById() get your ViewPager2 instance
val viewPager2 = findViewById(R.id.viewpager2) val viewPager2ViewHeightAnimator = ViewPager2ViewHeightAnimator() viewPager2ViewHeightAnimator.viewPager2 = viewPager2
how to i Pass your viewpager to viewpager2 variable.?
this is for wrapping the height of each view
import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 class ViewPager2ViewHeightAnimator { var viewPager2: ViewPager2? = null; set(value) { if (field != value) { field?.unregisterOnPageChangeCallback(onPageChangeCallback) field = value value?.registerOnPageChangeCallback(onPageChangeCallback) } } private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback(){ override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) recalculate(position, positionOffset) } } fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply { val leftView = findViewByPosition(position) ?: return@apply val rightView = findViewByPosition(position + 1) viewPager2?.apply { val leftHeight = getMeasuredViewHeightFor(leftView) layoutParams = layoutParams.apply { height = if (rightView != null) { val rightHeight = getMeasuredViewHeightFor(rightView) leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt() } else { leftHeight } } invalidate() } } private fun getMeasuredViewHeightFor(view: View) : Int { val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY) val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) view.measure(wMeasureSpec, hMeasureSpec) return view.measuredHeight } }
I do some improvements on it to change the height of viewpager if view height changes on runtime:
class ViewPager2ViewHeightAnimator { var viewPager2: ViewPager2? = null; set(value) { if (field != value) { field?.unregisterOnPageChangeCallback(onPageChangeCallback) field = value value?.registerOnPageChangeCallback(onPageChangeCallback) } } private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() { override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) recalculate(position, positionOffset) } } fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply { val leftView = findViewByPosition(position) ?: return@apply val rightView = findViewByPosition(position + 1) val setMeasure = { viewPager2?.apply { val leftHeight = getMeasuredViewHeightFor(leftView) layoutParams = layoutParams.apply { height = if (rightView != null) { val rightHeight = getMeasuredViewHeightFor(rightView) leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt() } else { leftHeight } } invalidate() } } val onLayoutChanged = ViewTreeObserver.OnGlobalLayoutListener { setMeasure.invoke() } leftView.viewTreeObserver.addOnGlobalLayoutListener(onLayoutChanged) rightView?.viewTreeObserver?.addOnGlobalLayoutListener(onLayoutChanged) setMeasure.invoke() } private fun getMeasuredViewHeightFor(view: View): Int { val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY) val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) view.measure(wMeasureSpec, hMeasureSpec) return view.measuredHeight } }
I encountered a bug when applying this solution: the first fragment in viewPager2 is always match_parent
when just entering the page
And, I solved it by making OnGlobalLayoutListener
disposable
- Add an extension for convenience
fun ViewTreeObserver.addDisposableOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener) {
addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
removeOnGlobalLayoutListener(this)
listener.onGlobalLayout()
}
})
}
- Replace the following code
val onLayoutChanged =
ViewTreeObserver.OnGlobalLayoutListener {
setMeasure.invoke()
}
leftView.viewTreeObserver.addOnGlobalLayoutListener(onLayoutChanged)
rightView?.viewTreeObserver?.addOnGlobalLayoutListener(onLayoutChanged)
with
leftView.viewTreeObserver.addDisposableOnGlobalLayoutListener { setMeasure.invoke() }
rightView?.viewTreeObserver?.addDisposableOnGlobalLayoutListener { setMeasure.invoke() }
how i pas in my view pager, please help me
On Fri, Jul 16, 2021 at 9:16 PM Owen Chen @.***> wrote:
this is for wrapping the height of each view
import android.view.Viewimport androidx.recyclerview.widget.LinearLayoutManagerimport androidx.recyclerview.widget.RecyclerViewimport androidx.viewpager2.widget.ViewPager2
class ViewPager2ViewHeightAnimator {
var viewPager2: ViewPager2? = null; set(value) { if (field != value) { field?.unregisterOnPageChangeCallback(onPageChangeCallback) field = value value?.registerOnPageChangeCallback(onPageChangeCallback) } } private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback(){ override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) recalculate(position, positionOffset) } } fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply { val leftView = findViewByPosition(position) ?: ***@***.*** val rightView = findViewByPosition(position + 1) viewPager2?.apply { val leftHeight = getMeasuredViewHeightFor(leftView) layoutParams = layoutParams.apply { height = if (rightView != null) { val rightHeight = getMeasuredViewHeightFor(rightView) leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt() } else { leftHeight } } invalidate() } } private fun getMeasuredViewHeightFor(view: View) : Int { val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY) val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) view.measure(wMeasureSpec, hMeasureSpec) return view.measuredHeight }
}
I do some improvements on it to change the height of viewpager if view height changes on runtime:
class ViewPager2ViewHeightAnimator {
var viewPager2: ViewPager2? = null; set(value) { if (field != value) { field?.unregisterOnPageChangeCallback(onPageChangeCallback) field = value value?.registerOnPageChangeCallback(onPageChangeCallback) } } private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() { override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) recalculate(position, positionOffset) } } fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply { val leftView = findViewByPosition(position) ?: ***@***.*** val rightView = findViewByPosition(position + 1) val setMeasure = { viewPager2?.apply { val leftHeight = getMeasuredViewHeightFor(leftView) layoutParams = layoutParams.apply { height = if (rightView != null) { val rightHeight = getMeasuredViewHeightFor(rightView) leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt() } else { leftHeight } } invalidate() } } val onLayoutChanged = ViewTreeObserver.OnGlobalLayoutListener { setMeasure.invoke() } leftView.viewTreeObserver.addOnGlobalLayoutListener(onLayoutChanged) rightView?.viewTreeObserver?.addOnGlobalLayoutListener(onLayoutChanged) setMeasure.invoke() } private fun getMeasuredViewHeightFor(view: View): Int { val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY) val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) view.measure(wMeasureSpec, hMeasureSpec) return view.measuredHeight }
}
I encountered a bug when applying this solution: the first fragment in viewPager2 is always match_parent when just entering the page
And, I solved it by making OnGlobalLayoutListener disposable
- Add an extension for convenience
fun ViewTreeObserver.addDisposableOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener) { addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { removeOnGlobalLayoutListener(this) listener.onGlobalLayout() } }) }
- Replace the following code
val onLayoutChanged = ViewTreeObserver.OnGlobalLayoutListener { setMeasure.invoke() } leftView.viewTreeObserver.addOnGlobalLayoutListener(onLayoutChanged) rightView?.viewTreeObserver?.addOnGlobalLayoutListener(onLayoutChanged)
with
leftView.viewTreeObserver.addDisposableOnGlobalLayoutListener { setMeasure.invoke() } rightView?.viewTreeObserver?.addDisposableOnGlobalLayoutListener { setMeasure.invoke() }
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/android/views-widgets-samples/issues/184#issuecomment-881442535, or unsubscribe https://github.com/notifications/unsubscribe-auth/APTH7ZO3D5Y6ZS7LDV6ZFD3TYAWL5ANCNFSM4RA4QZGQ .
Try this. This is working very well Put this in the fragment which is being used in viewPager
override fun onResume() {
super.onResume()
binding.root.requestLayout()
}
Thanks guys for help this problem.
this is for wrapping the height of each view
import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 class ViewPager2ViewHeightAnimator { var viewPager2: ViewPager2? = null; set(value) { if (field != value) { field?.unregisterOnPageChangeCallback(onPageChangeCallback) field = value value?.registerOnPageChangeCallback(onPageChangeCallback) } } private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback(){ override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) recalculate(position, positionOffset) } } fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply { val leftView = findViewByPosition(position) ?: return@apply val rightView = findViewByPosition(position + 1) viewPager2?.apply { val leftHeight = getMeasuredViewHeightFor(leftView) layoutParams = layoutParams.apply { height = if (rightView != null) { val rightHeight = getMeasuredViewHeightFor(rightView) leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt() } else { leftHeight } } invalidate() } } private fun getMeasuredViewHeightFor(view: View) : Int { val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY) val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) view.measure(wMeasureSpec, hMeasureSpec) return view.measuredHeight } }
I do some improvements on it to change the height of viewpager if view height changes on runtime:
class ViewPager2ViewHeightAnimator { var viewPager2: ViewPager2? = null; set(value) { if (field != value) { field?.unregisterOnPageChangeCallback(onPageChangeCallback) field = value value?.registerOnPageChangeCallback(onPageChangeCallback) } } private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() { override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) recalculate(position, positionOffset) } } fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply { val leftView = findViewByPosition(position) ?: return@apply val rightView = findViewByPosition(position + 1) val setMeasure = { viewPager2?.apply { val leftHeight = getMeasuredViewHeightFor(leftView) layoutParams = layoutParams.apply { height = if (rightView != null) { val rightHeight = getMeasuredViewHeightFor(rightView) leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt() } else { leftHeight } } invalidate() } } val onLayoutChanged = ViewTreeObserver.OnGlobalLayoutListener { setMeasure.invoke() } leftView.viewTreeObserver.addOnGlobalLayoutListener(onLayoutChanged) rightView?.viewTreeObserver?.addOnGlobalLayoutListener(onLayoutChanged) setMeasure.invoke() } private fun getMeasuredViewHeightFor(view: View): Int { val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY) val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) view.measure(wMeasureSpec, hMeasureSpec) return view.measuredHeight } }
I encountered a bug when applying this solution: the first fragment in viewPager2 is always
match_parent
when just entering the pageAnd, I solved it by making
OnGlobalLayoutListener
disposable
- Add an extension for convenience
fun ViewTreeObserver.addDisposableOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener) { addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { removeOnGlobalLayoutListener(this) listener.onGlobalLayout() } }) }
- Replace the following code
val onLayoutChanged = ViewTreeObserver.OnGlobalLayoutListener { setMeasure.invoke() } leftView.viewTreeObserver.addOnGlobalLayoutListener(onLayoutChanged) rightView?.viewTreeObserver?.addOnGlobalLayoutListener(onLayoutChanged)
with
leftView.viewTreeObserver.addDisposableOnGlobalLayoutListener { setMeasure.invoke() } rightView?.viewTreeObserver?.addDisposableOnGlobalLayoutListener { setMeasure.invoke() }
Hello, what you have posted has helped me but I have a performance problem and it is that the animation of the tablayout fails, any idea how to solve this? I have basically my view built with a viewpager 2 and a tablaout but when I add this listener the animation gets slow and not clean
this is for wrapping the height of each view
import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 class ViewPager2ViewHeightAnimator { var viewPager2: ViewPager2? = null; set(value) { if (field != value) { field?.unregisterOnPageChangeCallback(onPageChangeCallback) field = value value?.registerOnPageChangeCallback(onPageChangeCallback) } } private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback(){ override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) recalculate(position, positionOffset) } } fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply { val leftView = findViewByPosition(position) ?: return@apply val rightView = findViewByPosition(position + 1) viewPager2?.apply { val leftHeight = getMeasuredViewHeightFor(leftView) layoutParams = layoutParams.apply { height = if (rightView != null) { val rightHeight = getMeasuredViewHeightFor(rightView) leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt() } else { leftHeight } } invalidate() } } private fun getMeasuredViewHeightFor(view: View) : Int { val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY) val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) view.measure(wMeasureSpec, hMeasureSpec) return view.measuredHeight } }
I do some improvements on it to change the height of viewpager if view height changes on runtime:
class ViewPager2ViewHeightAnimator { var viewPager2: ViewPager2? = null; set(value) { if (field != value) { field?.unregisterOnPageChangeCallback(onPageChangeCallback) field = value value?.registerOnPageChangeCallback(onPageChangeCallback) } } private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() { override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) recalculate(position, positionOffset) } } fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply { val leftView = findViewByPosition(position) ?: return@apply val rightView = findViewByPosition(position + 1) val setMeasure = { viewPager2?.apply { val leftHeight = getMeasuredViewHeightFor(leftView) layoutParams = layoutParams.apply { height = if (rightView != null) { val rightHeight = getMeasuredViewHeightFor(rightView) leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt() } else { leftHeight } } invalidate() } } val onLayoutChanged = ViewTreeObserver.OnGlobalLayoutListener { setMeasure.invoke() } leftView.viewTreeObserver.addOnGlobalLayoutListener(onLayoutChanged) rightView?.viewTreeObserver?.addOnGlobalLayoutListener(onLayoutChanged) setMeasure.invoke() } private fun getMeasuredViewHeightFor(view: View): Int { val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY) val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) view.measure(wMeasureSpec, hMeasureSpec) return view.measuredHeight } }
I encountered a bug when applying this solution: the first fragment in viewPager2 is always
match_parent
when just entering the page And, I solved it by makingOnGlobalLayoutListener
disposable
- Add an extension for convenience
fun ViewTreeObserver.addDisposableOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener) { addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { removeOnGlobalLayoutListener(this) listener.onGlobalLayout() } }) }
- Replace the following code
val onLayoutChanged = ViewTreeObserver.OnGlobalLayoutListener { setMeasure.invoke() } leftView.viewTreeObserver.addOnGlobalLayoutListener(onLayoutChanged) rightView?.viewTreeObserver?.addOnGlobalLayoutListener(onLayoutChanged)
with
leftView.viewTreeObserver.addDisposableOnGlobalLayoutListener { setMeasure.invoke() } rightView?.viewTreeObserver?.addDisposableOnGlobalLayoutListener { setMeasure.invoke() }
Hello, what you have posted has helped me but I have a performance problem and it is that the animation of the tablayout fails, any idea how to solve this? I have basically my view built with a viewpager 2 and a tablaout but when I add this listener the animation gets slow and not clean
This method has been deprecated, I suggest using: view.requireView() on onResume method of viewpager2 fragments.
class ViewPager2ViewHeightAnimator ...
It works but I am presenting performance problems when tabbing or changing pages, each time I change the scroll of the first fragment becomes slower
this is for wrapping the height of each view
import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 class ViewPager2ViewHeightAnimator { var viewPager2: ViewPager2? = null; set(value) { if (field != value) { field?.unregisterOnPageChangeCallback(onPageChangeCallback) field = value value?.registerOnPageChangeCallback(onPageChangeCallback) } } private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback(){ override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) recalculate(position, positionOffset) } } fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply { val leftView = findViewByPosition(position) ?: return@apply val rightView = findViewByPosition(position + 1) viewPager2?.apply { val leftHeight = getMeasuredViewHeightFor(leftView) layoutParams = layoutParams.apply { height = if (rightView != null) { val rightHeight = getMeasuredViewHeightFor(rightView) leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt() } else { leftHeight } } invalidate() } } private fun getMeasuredViewHeightFor(view: View) : Int { val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY) val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) view.measure(wMeasureSpec, hMeasureSpec) return view.measuredHeight } }
I do some improvements on it to change the height of viewpager if view height changes on runtime:
class ViewPager2ViewHeightAnimator { var viewPager2: ViewPager2? = null; set(value) { if (field != value) { field?.unregisterOnPageChangeCallback(onPageChangeCallback) field = value value?.registerOnPageChangeCallback(onPageChangeCallback) } } private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() { override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) recalculate(position, positionOffset) } } fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply { val leftView = findViewByPosition(position) ?: return@apply val rightView = findViewByPosition(position + 1) val setMeasure = { viewPager2?.apply { val leftHeight = getMeasuredViewHeightFor(leftView) layoutParams = layoutParams.apply { height = if (rightView != null) { val rightHeight = getMeasuredViewHeightFor(rightView) leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt() } else { leftHeight } } invalidate() } } val onLayoutChanged = ViewTreeObserver.OnGlobalLayoutListener { setMeasure.invoke() } leftView.viewTreeObserver.addOnGlobalLayoutListener(onLayoutChanged) rightView?.viewTreeObserver?.addOnGlobalLayoutListener(onLayoutChanged) setMeasure.invoke() } private fun getMeasuredViewHeightFor(view: View): Int { val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY) val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) view.measure(wMeasureSpec, hMeasureSpec) return view.measuredHeight } }
the improvements that you made are dangerous to use in fragments, as their view can still be referenced by registering them as globalLayoutListeners. (fyi)