windowSoftInputMode="adjustResize" not working with translucent action/navbar

111,747

Solution 1

You are missing the following property:

android:fitsSystemWindows="true"

in the root RelativeLayout of the fragment .xml layout.

Update:

Last year there was an interesting talk by Chris Bane that explains in good detail how this works:

https://www.youtube.com/watch?v=_mGDMVRO3iE

Solution 2

There's a related bug report here. I've found a workaround that, from limited testing, seems to do the trick with no repercussions. Add a custom implementation of your root ViewGroup (I almost always am using FrameLayout, so this is what I've tested with) with the logic below. Then, use this custom layout in place of your root layout, and ensure you set android:fitsSystemWindows="true". You can then just call getInsets() any time after layout (e.g. add an OnPreDrawListener) to adjust the rest of your layout to account for the system insets, if desired.

import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import org.jetbrains.annotations.NotNull;

/**
 * @author Kevin
 *         Date Created: 3/7/14
 *
 * https://code.google.com/p/android/issues/detail?id=63777
 * 
 * When using a translucent status bar on API 19+, the window will not
 * resize to make room for input methods (i.e.
 * {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE} and
 * {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_PAN} are
 * ignored).
 * 
 * To work around this; override {@link #fitSystemWindows(android.graphics.Rect)},
 * capture and override the system insets, and then call through to FrameLayout's
 * implementation.
 * 
 * For reasons yet unknown, modifying the bottom inset causes this workaround to
 * fail. Modifying the top, left, and right insets works as expected.
 */
public final class CustomInsetsFrameLayout extends FrameLayout {
    private int[] mInsets = new int[4];

    public CustomInsetsFrameLayout(Context context) {
        super(context);
    }

    public CustomInsetsFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomInsetsFrameLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public final int[] getInsets() {
        return mInsets;
    }

    @Override
    protected final boolean fitSystemWindows(@NotNull Rect insets) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            // Intentionally do not modify the bottom inset. For some reason, 
            // if the bottom inset is modified, window resizing stops working.
            // TODO: Figure out why.

            mInsets[0] = insets.left;
            mInsets[1] = insets.top;
            mInsets[2] = insets.right;

            insets.left = 0;
            insets.top = 0;
            insets.right = 0;
        }

        return super.fitSystemWindows(insets);
    }
}

Since fitSystemWindows was deprecated, please refer to the answer below to complete the workaround.

Solution 3

@kcoppock answer is really helpful, but fitSystemWindows was deprecated in API level 20

So since API 20 (KITKAT_WATCH) you should override onApplyWindowInsets

@Override
public final WindowInsets onApplyWindowInsets(WindowInsets insets) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
        return super.onApplyWindowInsets(insets.replaceSystemWindowInsets(0, 0, 0,
                insets.getSystemWindowInsetBottom()));
    } else {
        return insets;
    }
}

Solution 4

If you want to customize the insets and you are targeting API level >=21 you can accomplish this without having to create a custom view group. By just setting fitsSystemWindows padding will be applied to your container view by default, which you may not want.

The version checks are built into this method and only devices >= 21 will execute the code inside the lambda. Kotlin example:

ViewCompat.setOnApplyWindowInsetsListener(container) { view, insets ->
  insets.replaceSystemWindowInsets(0, 0, 0, insets.systemWindowInsetBottom).apply {
    ViewCompat.onApplyWindowInsets(view, this)
  }
}

Make sure your layout still sets the fitsSystemWindows flag otherwise the window insets listener will not be called.

<FrameLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    />

These sources are helpful:

https://medium.com/google-developers/why-would-i-want-to-fitssystemwindows-4e26d9ce1eec https://medium.com/@azizbekian/windowinsets-24e241d4afb9

Solution 5

This worked for me to have translucent status bar and adjustResize in fragment:

  1. Make a custom RelativeLayout as @Victor91 and @kcoppock said.

  2. Use CustomRelativeLayout as parent layout for your fragment.

  3. Declare theme with android:windowTranslucentStatus = true

  4. The container Activity must be declared in Manifest with android:windowSoftInputMode="adjustResize" and use the declared theme

  5. Please Use fitsSystemWindows on fragment root layout!

    public class CustomRelativeLayout extends RelativeLayout {
    
        private int[] mInsets = new int[4];
    
        public CustomRelativeLayout(Context context) {
            super(context);
        }
    
        public CustomRelativeLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public CustomRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        public CustomRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
        }
    
        @Override
        public final WindowInsets onApplyWindowInsets(WindowInsets insets) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
                mInsets[0] = insets.getSystemWindowInsetLeft();
                mInsets[1] = insets.getSystemWindowInsetTop();
                mInsets[2] = insets.getSystemWindowInsetRight();
                return super.onApplyWindowInsets(insets.replaceSystemWindowInsets(0, 0, 0,
                        insets.getSystemWindowInsetBottom()));
            } else {
                return insets;
            }
        }
    }
    

Then in xml,

<com.blah.blah.CustomRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:fitsSystemWindows="true">
</com.blah.blah.CustomRelativeLayout>
Share:
111,747

Related videos on Youtube

fabianbru
Author by

fabianbru

Updated on July 08, 2022

Comments

  • fabianbru
    fabianbru almost 2 years

    I have problems with the translucent actionbar/navbar in the new Android KitKat (4.4) and the windowSoftInputMode="adjustResize".

    Normaly, changing the InputMode to adjustResize, the app should resize itself when keyboard is shown, but here it won't! If I delete the lines for the transparent effect, the resize is working.

    So if the keyboard is visible, my ListView is under it and I can't access the last few items (only by hiding the keyboard manually).

    AndroidManifest.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="XYZ"
    android:versionCode="23"
    android:versionName="0.1" >
    
    <uses-sdk
        android:minSdkVersion="9"
        android:targetSdkVersion="19" />
    
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.XYZStyle" >
        <activity
            android:name="XYZ"
            android:label="@string/app_name"
            android:windowSoftInputMode="adjustResize" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    
    </manifest>
    

    values-v19/styles.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
    <style name="Theme.XYZStyle" parent="@style/Theme.AppCompat.Light">
        <item name="android:windowTranslucentStatus">true</item>
        <item name="android:windowTranslucentNavigation">true</item>
    </style>
    
    </resources>
    

    fragment.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
    <ListView
        android:id="@+id/listView_contacts"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        android:divider="@null"
        android:dividerHeight="0dp"
        android:drawSelectorOnTop="true"
        android:fastScrollAlwaysVisible="true"
        android:fastScrollEnabled="true"
        android:paddingBottom="@dimen/navigationbar__height" >
    </ListView>
    
    </RelativeLayout>
    

    Any ideas for fixing this?

  • sealskej
    sealskej over 9 years
    Actually SOFT_INPUT_ADJUST_PAN seems to be NOT ignored according to my experience - it will move whole screen up, including system bar and shift keyboard under focused view.
  • Simon
    Simon almost 9 years
    Thanks - you are correct about the SOFT_INPUT_ADJUST_PAN. I used this in my fragment: getActivity().getWindow().setSoftInputMode(WindowManager.Lay‌​outParams.SOFT_INPUT‌​_ADJUST_PAN);
  • Lucas
    Lucas about 8 years
    That was the only way I could achieve having adjustResize for activity (needed for the scrolling of the view upon showing keyboard), fitSystemWindows set as true so that the scroll actually happens on >= Lollipop and having translucent statusBar. Thanks a lot.
  • Paulina
    Paulina almost 6 years
    This is the best answer so far, and I'm looking for solution some time now. It's working perfectly, but you have to add some extra padding to your toolbar, without it your toolbar will be overlapping statusbar
  • Bogdan Zurac
    Bogdan Zurac about 5 years
    This still remains the best approach, since you can apply this is your BaseFragment, along with view.fitsSystemWindows = true and it just works without any changes to the actual XML layouts or View subclasses.
  • Onik
    Onik over 3 years
    Wanted to note that the solution may not work for some view acting as container (in my case it was ConstraintLayout). The fix was to wrap my layout with FrameLayout that became a new container. Appreciate the solution!
  • mikep
    mikep over 3 years
    I do not see difference between your solution and @kcoppock answer. Tested both. When I use your method the same todo items 1 - 5 including fitSystemWindows of kcoppock answer must be used. When I omit fitSystemWindows status bar is translucent but windowSoftInputMode=“adjustResize” is not working (I do not see input in which im writing)
  • Andrey Kijonok
    Andrey Kijonok over 2 years
    I almost got depression. I thought only I have issues with transparent status bar. And after 2 hours of searching. Finally got your answer. You saved my day. Thank you!
  • Admin
    Admin almost 2 years
    This code working when you are use full screen flag in your activity Thank you