setOnApplyWindowInsetsListener never called
Asked Answered
Q

14

24

I would like to calculate the navigationBar height. I've seen this presentation : https://chris.banes.me/talks/2017/becoming-a-master-window-fitter-nyc/

So, I tried to use the method View.setOnApplyWindowInsetsListener(). But, for some reason, it's never called.

Does anyone knows why ? Any limitation there ?

I've tried to use it like this :

navBarOverlay.setOnApplyWindowInsetsListener { v, insets -> 
   Timber.i("BOTTOM = ${insets.systemWindowInsetBottom}")
   return@setOnApplyWindowInsetsListener insets
}

Note that my root layout is a ConstraintLayout.

Quote answered 6/1, 2019 at 15:7 Comment(2)
I add the same issue. For api > 28 everything works fine, but for api 26 the listener is never called.Renner
Did u found a solution to the problem?Beryl
E
13

I faced the same issue.

If your root view is ConstraintLayout and contains android:fitsSystemWindows="true" attr, the view consumed onApplyWindowInsets callbacks. So if you set onApplyWindowInsets on child views, they never get onApplyWindowInsets callbacks.

Or check your parent views consume the callback.

Eardrop answered 21/11, 2019 at 9:41 Comment(3)
I know it's too late, just asking here if someone knows the answer. What should we do in such cases, if we want one of the child views to receive callback to onApplyWindowInsets?Keys
The way I did is I set it on the ConstraintLayout and modify the child there. ViewCompat.setOnApplyWindowInsetsListener(binding.constraintLayout) { view, insets -> val insetsTop = insets.getInsets(WindowInsetsCompat.Type.systemBars()).top binding.frameLayoutContainer.updatePadding(top = insetsTop) WindowInsetsCompat.CONSUMED }Elissa
Actually, I try again and you can try to call setOnApplyWindowInsetsListener on the root layout but return the insets. And then call it again on your child view, do what you need to do here, and return WindowInsetsCompat.CONSUMED.Elissa
S
9

I have faced with this problem when I've used CollapsingToolbarLayout, problem is that CollapsingToolbarLayout not invoking insets listener, if you have CollapsingToolbarLayout, then right after this component all other view insets wouldn't be triggered. If so, then remove listener from CollapsingToolbarLayout by calling

ViewCompat.setOnApplyWindowInsetsListener(collapsingToolbarLayout, null)

If you don't CollapsingToolbarLayout, then some other view is blocking insets from passing from view to view.

Or you have already consumed them, I guess you didn't do it)

Sauger answered 5/11, 2020 at 12:26 Comment(1)
In my case on android 10+ setOnApplyWindowInsetsListener worked fine, but on android 7 it did not. It helped me.Garica
M
8

This is what I observed; in other words, your experience might be different.

[Layout for Activity]
<androidx.coordinatorlayout.widget.CoordinatorLayout
    android:id="@+id/coordinatorLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"    <--
    tools:context=".MyAppActivity">

    ...

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

Notice android:fitsSystemWindows="true" in the outer most layout. As long as we have it, setOnApplyWindowInsetsListener() does get called.

class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...

        ViewCompat.setOnApplyWindowInsetsListener(fab) { view, insets ->
            ...
            insets
        }
    }

Alternatively, if you are going for the "full screen", meaning you want your layout to extend to the status bar and the navigation bar, you can do something like the following.

override fun onCreate(savedInstanceState: Bundle?) {
    ...

    WindowCompat.setDecorFitsSystemWindows(window, false)    <--

    ViewCompat.setOnApplyWindowInsetsListener(fab) { view, insets ->
        ...
        insets
    }
}

The same idea is applicable when you are using a Fragment, as long as the Activity (that contains the Fragment) has either fitsSystemWindows in the outer most layout or you set your Activity as full screen.

Medley answered 4/11, 2020 at 19:9 Comment(1)
Be sure to be using AndroidX SearchView, i wasted too much time until discovering this works, just using the correct SearchView package.: androidx.appcompat.widget.SearchView , i did not see it at first.Lancet
I
3

There is also bug with CollapsingToolbarLayout, it prevents siblings to receive insets, you can see it in issues github link. One of the solutions is to putAppbarLayout below in xml other views for them to receive insets.

Intersect answered 6/11, 2020 at 6:5 Comment(0)
P
3

I faced similar issue on API 30. For setOnApplyWindowInsetsListener() to work you have to make sure that your activity is in full-screen mode. You can use below method to do so

WindowCompat.setDecorFitsSystemWindows(activity.window, false) //this is backward compatible version

Also make sure you are not using below method anywhere to set UI flags

View.setSystemUiVisibility(int visibility)
Philanthropy answered 23/6, 2021 at 9:49 Comment(0)
P
2

I've had to (and I think I am expected to) explicitly call requestApplyInsets() at some appropriate time to make the listener get hit.

Check this article for some possible tips: https://medium.com/androiddevelopers/windowinsets-listeners-to-layouts-8f9ccc8fa4d1

Pituri answered 22/5, 2019 at 17:34 Comment(0)
B
2

I found a solution for my case on APIs from 28 to 26. Because on higher versions everything was fine.

First, I need to mention, that I've tried everything mentioned in the answers above but nothing helped.

The solution:

ViewCompat.setOnApplyWindowInsetsListener(your_parent_view) { v, insets ->
    var consumed = false

    (v as ViewGroup).forEach { child ->
        // Dispatch the insets to the child
        val childResult = ViewCompat.dispatchApplyWindowInsets(child, insets)
        // If the child consumed the insets, record it
        if (childResult.isConsumed) {
            consumed = true
        }
    }

    // If any of the children consumed the insets, return an appropriate value
    if (consumed) WindowInsetsCompat.CONSUMED else insets
}

In my case your_parent_view was a FragmentViewContainer where my Fragment was located with the AppBarLayout to which I want to add padding.

It's not my finding, this is mentioned in Chris Banes's article, as a fix for another issue.

In addition, I've removed all the fitsSystemWindows=true lines and left this attribute only in my AppBarLayout, in the current fragment.

And of course, this line should be added to your Activity:

WindowCompat.setDecorFitsSystemWindows(window, false)

The additional ViewCompat.setOnApplyWindowInsetsListener(appBarLayout) was not required. The fitsSystemWindows=true attribute was doing its work.

Beryl answered 12/7, 2023 at 6:29 Comment(0)
S
1

My solution is to call it on navBarOverlay.rootView.

Surreptitious answered 14/8, 2020 at 9:36 Comment(0)
A
1

putting ViewCompat.setOnApplyWindowInsetsListener into onResume worked for me with constraintLayout.

@Override
public void onResume() {
    super.onResume();
    ViewCompat.setOnApplyWindowInsetsListener(requireActivity().getWindow().getDecorView(), (v, insets) -> {
            boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
            int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
            return insets;
        });
} 
Ansilme answered 2/6, 2021 at 10:40 Comment(1)
I think it might be related with my comment below. if there are multiple listeners set by setting the listener in onResume might be delaying the set and overriding the other listener.Monkhmer
G
1

I had this problem on android 7.1. But on android 11 it worked correctly. Just create a class:

import android.content.Context
import android.util.AttributeSet
import androidx.core.view.ViewCompat
import com.google.android.material.appbar.CollapsingToolbarLayout

class InsetsCollapsingToolbarLayout @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyle: Int = 0
) : CollapsingToolbarLayout(context, attrs, defStyle) {

    init {
        ViewCompat.setOnApplyWindowInsetsListener(this, null)
    }

}

And use everywhere InsetsCollapsingToolbarLayout instead of CollapsingToolbarLayout

Garica answered 24/9, 2021 at 13:20 Comment(0)
T
0

In my app, it gets called once and not every time I wanted to. Therefore, in that one time it gets called, I saved the widnowInsets to a global variable to use it throughout the app.

Transference answered 27/8, 2019 at 7:18 Comment(0)
P
0

I used the following solution using this answer:

ViewCompat.setOnApplyWindowInsetsListener(
        findViewById(android.R.id.content)
    ) { _: View?, insets: WindowInsetsCompat ->
        navigationBarHeight = insets.systemWindowInsetBottom
        insets
    }
Pewit answered 6/3, 2021 at 10:22 Comment(1)
This may work but if your toolbar has the custom color you'll need to change the status bar color accordingly. This may lead to hardcoded statusBar colors across the app where you have a different color of Toolbar.Beryl
W
0

I used following solution in my project and it's works like a charm.

val decorView: View = requireActivity().window.decorView
val rootView = decorView.findViewById<View>(android.R.id.content) as ViewGroup
ViewCompat.setOnApplyWindowInsetsListener(rootView) { _, insets ->
    val isKeyboardVisible = isKeyboardVisible(insets)
    Timber.d("isKeyboardVisible: $isKeyboardVisible")

    // Do something with isKeyboardVisible

    insets
}

private fun isKeyboardVisible(insets: WindowInsetsCompat): Boolean {
    val systemWindow = insets.systemWindowInsets
    val rootStable = insets.stableInsets
    if (systemWindow.bottom > rootStable.bottom) {
        // This handles the adjustResize case on < API 30, since
        // systemWindow.bottom is probably going to be the IME
        return true
    }
    return false
}

Use setWindowInsetsAnimationCallback instead of setOnApplyWindowInsetsListener in Android API > 30

Waylen answered 3/7, 2021 at 8:57 Comment(0)
M
0

One problem I had is ViewCompat.setOnApplyWindowInsetsListener was called again in some other place in the code for the same view. So make sure that is only set once.

Monkhmer answered 23/2, 2023 at 11:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.