setOnApplyInsetsListener always returns insets with 0
MobilefactoryAT opened this issue · 4 comments
I had the issue that the listener received window insets always with 0px
My layout looks like this and I applied the doOnApplyWindowInsets
to BottomNavigationView
.
I fixed it for me by waiting until it is attached and then listen for insets on the top most parent view.
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:fitsSystemWindows="false"
...>
<com.google.android.material.bottomnavigation.BottomNavigationView
... />
Fixed code in kotlin...
/**
* A wrapper around [ViewCompat.setOnApplyWindowInsetsListener] which stores the initial view state, and provides them whenever a
* [android.view.WindowInsets] instance is dispatched to the listener provided.
*
*
* This allows the listener to be able to append inset values to any existing view state
* properties, rather than overwriting them.
*/
fun setOnApplyInsetsListener(view: View, listener: OnApplyInsetsListener) {
val tagState = view.getTag(R.id.insetter_initial_state) as ViewState?
val initialState: ViewState
if (tagState != null) {
initialState = tagState
} else {
initialState = ViewState(view)
view.setTag(R.id.insetter_initial_state, initialState)
}
if (ViewCompat.isAttachedToWindow(view)) {
registerForWindowInsets(view, getMostParentView(view), initialState, listener)
} else {
view.addOnAttachStateChangeListener(
object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
v.removeOnAttachStateChangeListener(this)
registerForWindowInsets(v, getMostParentView(v), initialState, listener)
}
override fun onViewDetachedFromWindow(v: View) {
// no-op
}
})
}
}
/**
* Register for window insets.
* Commits result through wrapper.
*/
private fun registerForWindowInsets(
view: View,
mostParent: View,
initialState: ViewState,
listener: OnApplyInsetsListener
) {
ViewCompat.setOnApplyWindowInsetsListener(
mostParent
) { v, insets ->
listener.onApplyInsets(view, insets, initialState)
// Always return the initial insets instance
insets
}
ViewCompat.requestApplyInsets(mostParent)
}
/**
* Returns the most parent view in its hierarchy
*/
private fun getMostParentView(view: View): View =
view.parent.run {
if (this != null && this is View && this.id != android.R.id.content) {
getMostParentView(this)
} else view
}
Hello!
Could you share a complete example of the project?
Activity:
Insetter.setEdgeToEdgeSystemUiFlags(window.decorView, true)
Activity layout:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/navFragment"
android:fitsSystemWindows="true"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="@navigation/activity_home_navigation"
android:layout_height="match_parent"
android:layout_width="match_parent"/>
Fragment Layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
....
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false"
android:keepScreenOn="true">
<fragment
android:id="@+id/homeNavFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:defaultNavHost="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:navGraph="@navigation/home_content" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/mainBottomNavigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
app:elevation="8dp"
app:labelVisibilityMode="labeled"
android:visibility="visible"
app:hideOnScroll="true"
paddingBottomSystemWindowInsets="@{true}"
app:itemIconTint="@color/bottom_navigation_colors"
app:itemTextColor="@color/bottom_navigation_colors"
app:layout_behavior=".ui.fragments.BottomBehaviour"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/navigation" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{(view) -> viewModel.onFabClicked(view)}"
android:transitionName="upload"
android:tint="?attr/colorOnSecondary"
app:backgroundTint="?attr/colorSecondary"
android:fitsSystemWindows="true"
app:elevation="12dp"
android:visibility="visible"
app:layout_anchor="@id/mainBottomAppbarHidden"
app:layout_anchorGravity="center"
app:rippleColor="?attr/colorError"
app:srcCompat="@drawable/ic_fab_upload" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/snackbar_coordinator"
android:layout_gravity="top"
paddingTopSystemWindowInsets="@{true}"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
It makes no sense to use the attribute fitsSystemWindows everywhere
Here is a list of layouts that support it:
DrawerLayout, CoordinatorLayout, AppBarLayout, CollapsingToolbarLayout, NavigationView
You have a NavHostFragment (R.id.homeNavFragment). Maybe some of the nested fragments have already consumed the insets?
I don't think this is really an issue in Insetter, so marking as closed.