Search code examples
androidandroid-layoutandroid-softkeyboardappcompatdialogfragment

How to force the soft input to overlap bottom-anchored View?


TL;DR: soft keyboard should overlap a bottom-anchored view instead of pushing it up. Gitlab link for an mcve.

Summary:

I have an AppCompatDialogFragment (androidx) which appears fullscreen on phones and has fixed dimensions on tablets (using dialog?.window?.setLayout(width, height) in case this matters). The dialog's layout has some content placed in a ScrollView and a button-like layout anchored at the bottom (complete XML structure see below).

Side note: the superclass of the AppCompatDialogFragment in question calls setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)on its Window.

The problem:

When the soft keyboard appears as a result of some text input receiving focus, it pushes the complete layout up, including the bottom-anchored view that I want overlapped by the soft keyboard. For some reason, the issue only affects phones, on tablets the soft keyboard correctly overlaps everything including the bottom-anchored view without any additional adjustments or flags.

What I have tried (without any success):

  • setting android:windowSoftInputMode="adjustNothing" (tried other flags "just in case" as well) for the Activity in question, also tried to apply them in code
  • setting android:isScrollContainer="false" on the ScrollView and its parent
  • combinations of the above
  • looking for similar questions like this one and confirming the proposed solutions didn't work

Here's the layout in question (note: I omitted many unrelated attributes to keep the snippet reasonably sized; everything is positioned vertically matching the elements' order. The <include> elements contain the text inputs):

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.appcompat.widget.Toolbar         
        android:layout_width="match_parent"
        android:id="@+id/toolbar"
        android:layout_height="?attr/actionBarSize"/>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@id/bottom_layout"
        app:layout_constraintTop_toBottomOf="@id/toolbar"
        android:isScrollContainer="false"
        android:scrollbarStyle="outsideOverlay">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <include
                layout="@layout/some_layout_1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>

            <include
                layout="@layout/some_layout_2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>

        </androidx.constraintlayout.widget.ConstraintLayout>
    </ScrollView>

  <!-- I want this one to be overlapped by the soft input, but it's just pushed up -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:id="@+id/bottom_layout"
        app:layout_constraintBottom_toBottomOf="parent">

        <View
            android:layout_width="match_parent"
            android:layout_height="@dimen/divider"
            android:background="@color/dark_grey" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="@dimen/some_dimen"
            android:foreground="?attr/selectableItemBackground"
            android:text="@string/text" />
    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

The question: how do I force the soft keyboard to overlap the layout anchored at the bottom on all devices?

Please comment if you need any additional details.

EDIT: here's a minimal demo app that reproduces the issue: https://gitlab.com/Droidman/soft-keyboard-issue-demo


Solution

  • Try adding the following line to onCreateView() of DemoDialog:

    dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)

    It is important to apply this change to the window of the dialog and not to the window of the activity.

    With this in place, this is what I see on an Nexus 6 emulator running API 29 using your MCVE:

    enter image description here

    You may need to work with the placement a little, but this should help.


    Let's look under the hood a little. For a phone and a tablet, the soft input mode is zero in onCreateView(). This value corresponds to SOFT_INPUT_ADJUST_UNSPECIFIED. The documentation for SOFT_INPUT_ADJUST_PAN explains what happens when the soft input mode is unspecified (emphasis is mine.)

    Adjustment option for softInputMode : set to have a window pan when an input method is shown, so it doesn't need to deal with resizing but just panned by the framework to ensure the current input focus is visible. This can not be combined with SOFT_INPUT_ADJUST_RESIZE ; if neither of these are set, then the system will try to pick one or the other depending on the contents of the window.

    Looking at the value of the soft input mode in onStart() (dialog?.window?.attributes?.softInputMode) shows the value selected by the system for the phone is SOFT_INPUT_ADJUST_RESIZE) and the value selected for the tablet is SOFT_INPUT_ADJUST_PAN. This explains the difference that is seen between phones and tablets.

    So, always setting the soft input mode to SOFT_INPUT_ADJUST_PAN looks like the best solution although SOFT_INPUT_ADJUST_NOTHING also works and may be preferable for certain layouts.