androidandroid-layoutkeyboardandroid-softkeyboardandroid-styles

Can I have "adjustResize" soft keyboard behavior and a toolbar behind the status bar at the same time?


In my Android app, it is vital for me to use the adjustResize behavior for the soft keyboard. So users can scroll down to other UI elements, such as a "continue" button.

I've noticed that that adjustResize only works when I have both the Manifest setting and android:fitsSystemWindows="true" in the layout root element. (Please correct me if I'm wrong!)

But with android:fitsSystemWindows="true" the Toolbar no longer sits behind the Status Bar. Which makes perfect sense, but isn't what I want.

When the Toolbar sits behind it, the status bar has a matching darker shade of my Toolbar's color. What I have with android:fitsSystemWindows="true" is a colorless status bar and a toolbar that sits 24dp lower than I want it.

I will give up the matching colored Status Bar for the sake of the adjustResize keyboard behavior. But my question is, is it possible to have both? Dare I dream for both Beauty and Accessibility?

Anyone more experienced know the magical combination of settings? Or perhaps, as a work around, a way to explicitly color the status bar?

fyi:

These are Activities with RelativeLayout root elements, and there are ListViews and EditTexts in some of them.

Toolbar is android.support.v7.widget.Toolbar

Potentially relevant Style items:

<style name="AppBaseTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="android:windowContentOverlay">@null</item>

PS - I've read dozens of similar-ish questions on soft keyboard behavior, but was unable to find anything helpful on unintended effects to the Toolbar. Also vice versa, lots of Style questions about toolbar/statusbar behavior, but nothing seemingly relevant. Never the less, sorry if I missed something!

Many thanks in advance!

Edit

I've been playing with removing android:fitsSystemWindows="true" and adding more ScrollViews or trying to get everything into the same ScrollView. This does nothing.

If I remove android:fitsSystemWindows="true" then the bottom of the UI is "glued" to the bottom of the screen -- it does not "resize" to instead glue to the top of the soft keyboard like I would expect it to do with adjustResize set in the Manifest.

Setting android:fitsSystemWindows="true" in the root view makes the UI resize like I would expect -- but it also makes the toolbar no longer draw behind the statusBar.

So I am still exactly where I started :(

Adding a layout XML code sample:

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 <!-- CoordinatorLayout because this view uses SnackBars -->

    <!-- Relative Layout to lock "continue" button bar to bottom -->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- Main content, that scrolls with or without keyboard -->
        <!-- Correctly sits behind transparent Status Bar -->
        <android.support.v4.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingBottom="@dimen/footer_persistent_height">
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">
                <!-- ACTUAL VIEWS DELETED FOR BREVITY / CLARITY -->
            </RelativeLayout>
        </android.support.v4.widget.NestedScrollView>


        <!-- Bottom nav bar -->
        <!-- Correctly sits at bottom of UI when keyboard is not visible -->
        <!-- PROBLEM: Not accessible when soft keyboard is visible -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            style="?android:attr/buttonBarStyle">

            <Button
                android:id="@+id/skip_button"
                android:theme="@style/ButtonContinueGrey"
                android:onClick="skipClickHandler"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight=".5"
                style="?android:attr/buttonBarButtonStyle"/>

            <Button
                android:id="@+id/button_progress"
                android:theme="@style/ButtonContinueColored"
                android:onClick="continueClickHandler"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight=".5"
                style="?android:attr/buttonBarButtonStyle"/>
        </LinearLayout>

    </RelativeLayout>
</android.support.design.widget.CoordinatorLayout>

Solution

  • (Answering my own question)

    This does seem to be a bug in android: https://code.google.com/p/android/issues/detail?id=63777

    Posts on the above bug report link to a few suggested work arounds, like creating a custom Layout class or custom Soft Keyboard.

    I feel like I've already wasted enough time with this. So my solution is to just manually color the Status Bar.

    Solution:

    1. Set android:fitsSystemWindows="true" in either the Layout's root view or globally in style.xml. This (along with adjustResize in the manifest) makes the UI shrink above the Soft Keyboard so no UI is blocked -- the most important thing.

    2. Color the Status Bar. This is only possible in Lollypop and newer. Which is the same as the transparent StatusBar, anyway.

      private void updateStatusBarColor(){
          // Check Version
          if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
              // OPTIONAL: Calculate a darker version of the toolbar color
              int color = calculateDarkerColor(toolBarColor);
      
              // Get the statusBar
              Window window = getWindow();
              // You can't color a transparent statusbar
              window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
              window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
              // Set the color
              window.setStatusBarColor(color);
          }
      }
      
      // Calculate a darker version of a given color
      // Note I do this because the color always changes, if it's always the same I would save the darker version as a Resource in colors.xml
      private int calculateDarkerColor(int color){
          float[] hsv = new float[3];
          Color.colorToHSV(color, hsv);
          hsv[2] *= 0.8f; // smaller = darker
          return Color.HSVToColor(hsv);
      }