I have a FAB on a Fragment in a ViewPager2
which should respect the window insets when going edge-to-edge. I'm adding a OnApplyWindowInsetsListener
on the FAB which updates its margin. This works fine when using the old ViewPager
.
When updating to ViewPager2
it seems like the OnApplyWindowInsetsListener
is not called at the beginning. It is though, when I start the ActionMode
. Then, the listener is called and the new margin is used until I leave parent Fragment.
I've forked the demo project to illustrate the problem. See "ViewPager2 with Nested RecyclerViews" example (ParallelNestedScrollingActivity
) on the branch edge-to-edge
on https://github.com/hardysim/views-widgets-samples/tree/edge-to-edge .
In here, I've added a FAB to the (nested) RecyclerView
used on a ViewPager2-page and set the Activity-UI to edge-to-edge (see View.goEdgeToEdge()
). Then, the FAB is behind the navigation bar we need to update its margin to add the window insets.
And this is where it's not working (but it works fine with the old ViewPager
).
This has been answered in the issue tracker where it was originally asked:
The problem here is that the pages are not yet attached to the view hierarchy when the window insets are dispatched. The system doesn't call the
OnApplyWindowInsetsListener
with the current insets when a view is attached, so you'll have to callrequestApplyInsets()
when the view is attached to the hierarchy.
So I've created a little helper
/**
* Call this everytime when using [ViewCompat.setOnApplyWindowInsetsListener]
* to ensure that insets are always received.
*/
private fun View.requestApplyInsetsWhenAttached() {
// https://chris.banes.dev/2019/04/12/insets-listeners-to-layouts/
if (isAttachedToWindow) {
// We're already attached, just request as normal
requestApplyInsets()
} else {
// We're not attached to the hierarchy, add a listener to request when we are
addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
v.removeOnAttachStateChangeListener(this)
v.requestApplyInsets()
}
override fun onViewDetachedFromWindow(v: View) = Unit
})
}
}
which is called right after calling ViewCompat.setOnApplyWindowInsetsListener()
on a View
:
ViewCompat.setOnApplyWindowInsetsListener(view) { view, insets ->
// [do stuff]
insets
}
view.requestApplyInsetsWhenAttached()