Android How to adjust layout in Full Screen Mode when softkeyboard is visible Android How to adjust layout in Full Screen Mode when softkeyboard is visible android android

Android How to adjust layout in Full Screen Mode when softkeyboard is visible


Based on yghm's workaround, I coded up a convenience class that allows me to solve the problem with a one-liner (after adding the new class to my source code of course). The one-liner is:

     AndroidBug5497Workaround.assistActivity(this);

And the implementation class is:

public class AndroidBug5497Workaround {    // For more information, see https://issuetracker.google.com/issues/36911528    // To use this class, simply invoke assistActivity() on an Activity that already has its content view set.    public static void assistActivity (Activity activity) {        new AndroidBug5497Workaround(activity);    }    private View mChildOfContent;    private int usableHeightPrevious;    private FrameLayout.LayoutParams frameLayoutParams;    private AndroidBug5497Workaround(Activity activity) {        FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);        mChildOfContent = content.getChildAt(0);        mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {            public void onGlobalLayout() {                possiblyResizeChildOfContent();            }        });        frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();    }    private void possiblyResizeChildOfContent() {        int usableHeightNow = computeUsableHeight();        if (usableHeightNow != usableHeightPrevious) {            int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();            int heightDifference = usableHeightSansKeyboard - usableHeightNow;            if (heightDifference > (usableHeightSansKeyboard/4)) {                // keyboard probably just became visible                frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;            } else {                // keyboard probably just became hidden                frameLayoutParams.height = usableHeightSansKeyboard;            }            mChildOfContent.requestLayout();            usableHeightPrevious = usableHeightNow;        }    }    private int computeUsableHeight() {        Rect r = new Rect();        mChildOfContent.getWindowVisibleDisplayFrame(r);        return (r.bottom - r.top);    }}

Hope this helps someone.


Since the answer has already been picked and problem known to be a bug, I thought I would add a "Possible Work Around".

You can toggle fullScreen mode when soft keyboard is shown. This allows the "adjustPan" to work correctly.

In other words, I still use @android:style/Theme.Black.NoTitleBar.Fullscreen as part of the application theme and stateVisible|adjustResize as part of the activity window soft input mode but to get them to work together I must toggle fullscreen mode before the keyboard comes up.

Use the following Code:

Turn Off full screen mode

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

Turn On full screen mode

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);

Note - inspiration came from: Hiding Title in a Fullscreen mode


I tried the solution from Joseph Johnson, but like others I ran into the gap-between-content-and-keyboard problem. The problem occurs because the soft input mode is always pan when using full-screen mode. This panning interferes with Joseph's solution when you activate an input field that would be hidden by the soft input.

When the soft input appears, the content is first panned based on its original height, and then resized by the layout requested by the Joseph's solution. The resizing and subsequent layout do not undo the panning, which results in the gap. The full order of events is:

  1. Global layout listener
  2. Panning
  3. Layout of content (= actual resizing of content)

It is not possible to disable panning, but it is possible to force the pan offset to be 0 by changing the height of the content. This can be done in the listener, because it is run before panning takes place. Setting the content height to the available height results in a smooth user experience, i.e. no flickering.

I also made these changes. If any of these introduce issues, let me know:

  • Switched determination of available height to use getWindowVisibleDisplayFrame. The Rect is cached to prevent a little bit of unneeded garbage.
  • Allow the listener to be removed too. This is useful when you reuse an activity for different fragments having different full-screen requirements.
  • Do not distinguish between keyboard shown or hidden, but always set the content height to the visible display frame height.

It has been tested on a Nexus 5, and emulators running API levels 16-24 with screen sizes ranging from tiny to big.

The code has been ported to Kotlin, but porting my changes back to Java is simple. Let me know if you need help:

class AndroidBug5497Workaround constructor(activity: Activity) {    private val contentContainer = activity.findViewById(android.R.id.content) as ViewGroup    private val rootView = contentContainer.getChildAt(0)    private val rootViewLayout = rootView.layoutParams as FrameLayout.LayoutParams    private val viewTreeObserver = rootView.viewTreeObserver    private val listener = ViewTreeObserver.OnGlobalLayoutListener { possiblyResizeChildOfContent() }    private val contentAreaOfWindowBounds = Rect()    private var usableHeightPrevious = 0    // I call this in "onResume()" of my fragment    fun addListener() {        viewTreeObserver.addOnGlobalLayoutListener(listener)    }    // I call this in "onPause()" of my fragment    fun removeListener() {        viewTreeObserver.removeOnGlobalLayoutListener(listener)    }    private fun possiblyResizeChildOfContent() {        contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds)        val usableHeightNow = contentAreaOfWindowBounds.height()        if (usableHeightNow != usableHeightPrevious) {            rootViewLayout.height = usableHeightNow            // Change the bounds of the root view to prevent gap between keyboard and content, and top of content positioned above top screen edge.            rootView.layout(contentAreaOfWindowBounds.left, contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom)            rootView.requestLayout()            usableHeightPrevious = usableHeightNow        }    }}