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

29

197

I have researched a lot to adjust the layout when softkeyboard is active and I have successfully implemented it but the problem comes when I use android:theme="@android:style/Theme.NoTitleBar.Fullscreen" this in my activity tag in manifest file.

For this I have used android:windowSoftInputMode="adjustPan|adjustResize|stateHidden" with different options but no luck.

After that I implemented FullScreen programmatically and tried various layout to work with FullScreen but all in vain.

I referred these links and have looked many posts here related to this issue:

http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html

http://davidwparker.com/2011/08/30/android-how-to-float-a-row-above-keyboard/

Here is xml code:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/masterContainerView"
    android:layout_width="fill_parent" android:layout_height="fill_parent"
    android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="#ffffff">

    <ScrollView android:id="@+id/parentScrollView"
        android:layout_width="fill_parent" android:layout_height="wrap_content">

        <LinearLayout android:layout_width="fill_parent"
            android:layout_height="fill_parent" android:orientation="vertical">

            <TextView android:id="@+id/setup_txt" android:layout_width="wrap_content"
                android:layout_height="wrap_content" android:text="Setup - Step 1 of 3"
                android:textColor="@color/top_header_txt_color" android:textSize="20dp"
                android:padding="8dp" android:gravity="center_horizontal" />

            <TextView android:id="@+id/txt_header" android:layout_width="fill_parent"
                android:layout_height="40dp" android:text="AutoReply:"
                android:textColor="@color/top_header_txt_color" android:textSize="14dp"
                android:textStyle="bold" android:padding="10dp"
                android:layout_below="@+id/setup_txt" />

            <EditText android:id="@+id/edit_message"
                android:layout_width="fill_parent" android:layout_height="wrap_content"
                android:text="Some text here." android:textSize="16dp"
                android:textColor="@color/setting_editmsg_color" android:padding="10dp"
                android:minLines="5" android:maxLines="6" android:layout_below="@+id/txt_header"
                android:gravity="top" android:scrollbars="vertical"
                android:maxLength="132" />

            <ImageView android:id="@+id/image_bottom"
                android:layout_width="fill_parent" android:layout_height="wrap_content"
                android:layout_below="@+id/edit_message" />

        </LinearLayout>
    </ScrollView>

    <RelativeLayout android:id="@+id/scoringContainerView"
        android:layout_width="fill_parent" android:layout_height="50px"
        android:orientation="vertical" android:layout_alignParentBottom="true"
        android:background="#535254">

        <Button android:id="@+id/btn_save" android:layout_width="wrap_content"
            android:layout_height="wrap_content" android:layout_alignParentRight="true"
            android:layout_marginTop="7dp" android:layout_marginRight="15dp"
            android:layout_below="@+id/edit_message"
            android:text = "Save" />

        <Button android:id="@+id/btn_cancel" android:layout_width="wrap_content"
            android:layout_height="wrap_content" android:layout_marginTop="7dp"
            android:layout_marginRight="10dp" android:layout_below="@+id/edit_message"
            android:layout_toLeftOf="@+id/btn_save" android:text = "Cancel" />

    </RelativeLayout>
</RelativeLayout>

enter image description here

I want the bottom 2 buttons should go upward when the softkeyboard comes in picture.

enter image description here

Acquiesce answered 14/9, 2011 at 13:30 Comment(5)
I think you have to add Buttons inside ScrollView and below EditText.Scofflaw
I have already tried many options that does not work ...Acquiesce
put ur buttons in a framelayout and set the weight of the framelayout to 1 and finally use only android:windowSoftInputMode="adjustPan" tell me if this work..Teofilateosinte
@VineetShukla have you found any work out with full screen??Dev
Notice that you shouldn't use adjustResize and adjustPan at the same time, from the javadoc of android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE: "This can not be combined with{@link SOFT_INPUT_ADJUST_PAN}"Annunciata
R
278

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);
    }
}

Romeu answered 21/10, 2013 at 12:4 Comment(30)
Thank you, it worked for me ... I haven't had enough time to test it and see if it has some negative effect, but as a first impression it works ok. Thank you.Stoichiometric
This solution works for me when <item name="android:windowIsFloating">false</item> or else, when typing, a new column on keyboard will show up and will hide the one I want to display.Mariehamn
Yes, this solution works. People should see this before anything else. This works on S2, 4.1.1 and Nexus 4 (4.4)Stich
Thanks! I have no idea why, but I had to replace return (r.bottom - r.top); with return r.bottom to get it work on my HTC One Mini, otherwise the activity view would be pushed too high by the size of the status bar. I haven't tested it on another device yet though. Hope that can help.Osbourne
When should i call this class?Blueberry
Hi Joseph johnson, I used your code and it worked perfectly. But now days facing an issue on some small devices that it shows the gap (blank screen) between keyboard and layout. Have you any idea about this issue? I also tried return r.bottom.Cambridge
thanks, works almost perfect but one thing. does this solution take in account the losing of focus? on my implementation it doesn't restore the layout when the keyboard is dismissed (with hardware back button e.g.). have I done something wrong?Venn
onGlobalLayout doesn't get called when text edit loses focus - I've checked by setting a breakpoint.Venn
To Pankaj & heximal, sorry but I don't have time to investigate your questions. Hope you figure it out, and if so please report your findings.Romeu
Joseph Johnson : I have implemented your method, its working fine when we click on the top edit text but when we click on bottom edittext all design goes upDeb
@ranjith, see this SO question: #11410757Romeu
Unfortunately it doesn't work on Nexus 7 (2013). It still pans even with adjustNothing set.Dorkas
Wokred like a charm .. Even in fragments Thanks @JosephJohnsonMantle
I have a question: since the class is only instantiated in its constructor, when will the garbage collector delete that object?Shimkus
@Scorpio, good question. Note the call to addOnGlobalLayoutListener(). That call attaches the AndroidBug5497Workaround instance to the Activity. So the AndroidBug5497Workaround instance will not get garbage collected until the Activity gets collected (for example, if the user does not use the Activity for a long time).Romeu
@JosephJohnson thanks for the reply. I thought that addOnGlobalLayoutListener() was actually attached to the view represented through mChildOfContent. I actually thought it would be collected when the view is destroyed (which is not the same as the activity being destroyed if you use setContentView or other methods to change the views displayed by the activity).Shimkus
@Scorpio, you are correct. The listener is attached to the View, and the View is owned by the Activity. So when the Activity gets reclaimed, the View will also get reclaimed, and then the listener will also get reclaimed.Romeu
Thanks for the answer, it helped me solve my issue. I had to modify something though. I am using a Nexus tablet with Android Lollipop, and even though when the keyboard is visible this works as expected, when the keyboard is hidden my layout occupies the whole screen of the device (which is bad because there are the software buttons for home, back and tasks). How I fixed is, inside possiblyResizeChildOfContent() method, instead of doing any conditions I set rameLayoutParams.height = usableHeightNow. And for me this seems to work correctly everywhere.Volga
Reason I hate android: Bugs like this.. Reason I love android: Developers who provide workarounds like thisDisguise
After I added an action bar, I hat to subtract ActionBar.getHeight() from usableHeightSansKeyboard - heightDifference.W
This works BUT it has some qwerks. If you are setting the text size to anything other than default in an EditText that is using alignParentBottom="true" it will cause the view to create a gap above the keyboard and below the bottom part of the layout. (As others have mentioned) Using this answer (https://mcmap.net/q/36205/-how-to-check-visibility-of-software-keyboard-in-android) I added this condition if the keyboard is opened. if (editText != null) { editText.setTextSize(32); } And now I can go to sleep :)Ecbolic
Tried in an kiosk app with a webview (android 5) worked, you are awesomeDinnerware
Where can I give AndroidBug5497Workaround.assistActivity(this);, in onCreate?Prisilla
Awesome answer, thank you very much. It's working on a Nexus 6 but instead of using frameLayoutParams.height = usableHeightSansKeyboard; i have to use frameLayoutParams.height = usableHeightNow; If I don't this some elements falls outside the screen.Kelseykelsi
blank part appears above the keyboard. How to avoid this?Zak
@JosephJohnson Great stuff! Why is it divided by 4 usableHeightSansKeyboard / 4 ?Geneva
The /4 tells us that the usable height decreased by 1/4 or more, which is a good indication that the keyboard came up because keyboards typically require a fair amount of space. Clearly it's a dirty hack, but it has worked fairly well.Romeu
This works only in first fields. If all page is filled with fields it is not working proper.Fermata
it is working fine but when back pressed it shrinks the layout in xamarin android. how can i prevent layout to be shrink.Sulph
What a lovely piece of code! Thank you a lot! I had to tweak it just a little bit to make it work from within a fragment. Fragment was held by a FrameLayout, so all I did was replace android.R.id.content with R.id.frameLayoutFragmentHolder and it worked beautifuly. Thanks againWinne
F
45

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

Fylfot answered 8/6, 2012 at 16:8 Comment(4)
I appreciate that you have given time to the problem, +1 for that. I will definitely test this approach and let you know soon if it worked for me, thanks.Acquiesce
#4746488Fylfot
the keyboard snaps,making the keyboard background black. The snapping effect doesn't look good :(Sontag
wow thanks... it works for me really well by combining Workaround mentioned by AndroidBug5497Workaround ... I uploaded the combined source to GitHub...github.com/CrandellWS/AndroidBug5497Workaround/blob/master/…Exchequer
F
28

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
        }
    }
}
Flatware answered 15/2, 2017 at 22:14 Comment(7)
This seems to be the best answer. I ported to java here gist.github.com/grennis/2e3cd5f7a9238c59861015ce0a7c5584 . Note I was getting exceptions that observer was not alive, and had to check for that as well.Wick
Oh my god! been traversing all system views hierarchy looking for that Ghost space. I was close to ditch computers for a food truck but saw your answer in the last minute. It works :)Pride
@GregEnnis, thanks, your solution works with onResume(), onPause(), onDestroy() (see comments in GitHub code).Kris
This is working for me, except that the removeListener call doesn't seem to be working. I put breakpoints inside both the possiblyResizeChildOfContent call and the removeListener call, and even after i hit the removeListener breakpoint, possiblyResizeChildOfContent is still being called. Anyone else having this issue?Spoils
Okay, I fixed my issue by simply specifying the type of the listener like so private val listener = ViewTreeObserver.OnGlobalLayoutListener { possiblyResizeChildOfContent() }. I guess without the type it wasn't being removed properly.Spoils
How do we remove these window adjustments when moving to a new fragment? It works perfectly in the fragment where I have fullscreen mode but when I navigate back to the previous fragment which isnt full screen the bottom nav view is now hidden behind the device navigation. I guess whatever insets are being done in the full screen mode are carrying over to the prevouis fragment which doesnt require any window/inset modificationsLacefield
We would have to use viewTreeObserver.isAlive to ensure that viewTreeObserver is alive during the AndroidBug5497Workaround.removeListener() call. When tested on an Android 11 device, there is also a black rectangle above the keyboard that is the same height as the system status bar (contentAreaOfWindowBounds.top), which must be taken into account when determining the keyboard area size.Abbacy
A
16

I just found a simple and reliable solution if you are using the system UI approach (https://developer.android.com/training/system-ui/immersive.html).

It works in the case when you are using View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN, e.g. if you are using CoordinatorLayout.

It won't work for WindowManager.LayoutParams.FLAG_FULLSCREEN (The one you can also set in theme with android:windowFullscreen), but you can achieve similar effect with SYSTEM_UI_FLAG_LAYOUT_STABLE (which "has the same visual effect" according to the docs) and this solution should work again.

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION /* If you want to hide navigation */
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE)

I've tested it on my device running Marshmallow.

The key is that soft keyboards are also one of the system windows (such as status bar and navigation bar), so the WindowInsets dispatched by system contains accurate and reliable information about it.

For the use case such as in DrawerLayout where we are trying to draw behind the status bar, We can create a layout that ignores only the top inset, and applies the bottom inset which accounts for the soft keyboard.

Here is my custom FrameLayout:

/**
 * Implements an effect similar to {@code android:fitsSystemWindows="true"} on Lollipop or higher,
 * except ignoring the top system window inset. {@code android:fitsSystemWindows="true"} does not
 * and should not be set on this layout.
 */
public class FitsSystemWindowsExceptTopFrameLayout extends FrameLayout {

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

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

    public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs,
                                                 int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs,
                                                 int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            setPadding(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(),
                    insets.getSystemWindowInsetBottom());
            return insets.replaceSystemWindowInsets(0, insets.getSystemWindowInsetTop(), 0, 0);
        } else {
            return super.onApplyWindowInsets(insets);
        }
    }
}

And to use it:

<com.example.yourapplication.FitsSystemWindowsExceptTopFrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Your original layout here -->
</com.example.yourapplication.FitsSystemWindowsExceptTopFrameLayout>

This should theoretically work for any device without insane modification, much better than any hack that tries to take a random 1/3 or 1/4 of screen size as reference.

(It requires API 16+, but I'm using fullscreen only on Lollipop+ for drawing behind the status bar so it's the best solution in this case.)

Achromic answered 19/2, 2017 at 9:59 Comment(1)
Although the API needs to be modified, this is really a good solution. It feels very close to Google’s design philosophy. It’s a pity that no one liked it, maybe you didn’t explain it clearly enough.Immunogenic
A
10

Please note that android:windowSoftInputMode="adjustResize" does not work when WindowManager.LayoutParams.FLAG_FULLSCREENis set for an activity. You've got two options.

  1. Either disable fullscreen mode for your activity. Activity is not re-sized in fullscreen mode. You can do this either in xml (by changing the theme of the activity) or in Java code. Add the following lines in your onCreate() method.

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

OR

  1. Use an alternative way to achieve fullscreen mode. Add the following code in your onCreate() method.

    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
    getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
    View decorView = getWindow().getDecorView();
    // Hide the status bar.
    int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
    decorView.setSystemUiVisibility(uiOptions);`
    

Please note that method-2 only works in Android 4.1 and above.

Amazonite answered 6/3, 2015 at 10:9 Comment(3)
@AnshulTyagi method-2 only works in Android 4.1 and above.Amazonite
Tested on 5.0 and 4.4.2, Nexus 9 and Samsung s4 respectively, 2nd method does not work.Cryometer
2nd method simply does not work and I wasted a lot of time on it.Wick
D
9

I had to face this problem too and had a work around which i checked on HTC one, galaxy s1, s2, s3, note and HTC sensation.

put a global layout listener on the root view of your layout

mRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener(){
            public void onGlobalLayout() {
                checkHeightDifference();
            }
    });

and in there i checked the height difference and if the height difference of the screen is bigger then a third on the screen height then we can assume the keyboard is open. took it from this answer.

private void checkHeightDifference(){
    // get screen frame rectangle 
    Rect r = new Rect();
    mRootView.getWindowVisibleDisplayFrame(r);
    // get screen height
    int screenHeight = mRootView.getRootView().getHeight();
    // calculate the height difference
    int heightDifference = screenHeight - (r.bottom - r.top);

    // if height difference is different then the last height difference and
    // is bigger then a third of the screen we can assume the keyboard is open
    if (heightDifference > screenHeight/3 && heightDifference != mLastHeightDifferece) {
        // keyboard visiblevisible
        // get root view layout params
        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mRootView.getLayoutParams();
        // set the root view height to screen height minus the height difference
        lp.height = screenHeight - heightDifference;
        // call request layout so the changes will take affect
        .requestLayout();
        // save the height difference so we will run this code only when a change occurs.
        mLastHeightDifferece = heightDifference;
    } else if (heightDifference != mLastHeightDifferece) {
        // keyboard hidden
        PFLog.d("[ChatroomActivity] checkHeightDifference keyboard hidden");
        // get root view layout params and reset all the changes we have made when the keyboard opened.
        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mRootView.getLayoutParams();
        lp.height = screenHeight;
        // call request layout so the changes will take affect
        mRootView.requestLayout();
        // save the height difference so we will run this code only when a change occurs.
        mLastHeightDifferece = heightDifference;
    }
}

this is probably not bullet proof and maybe on some devices it will not work but it worked for me and hope it will help you too.

Deidredeific answered 15/8, 2013 at 8:42 Comment(1)
Needed some adjustements, but it worked. In the Nexus 7 2013 I had to decrease the keyboard height (screenHeight/3) by some pixels. Nice idea, thanks!Antivenin
V
9

Add android:fitsSystemWindows="true" to the layout, and this layout will resize.

Vacation answered 20/10, 2017 at 5:25 Comment(3)
That's what solved it for me. In addition thought, make sure you set it on the right view. If you have a background that should go below the status bar, don't set it there, but on a layout inside. Probably EditText views etc should be inside that second layout inside. Also watch this talk, as it makes things more clear: youtube.com/watch?v=_mGDMVRO3iELabuan
Worked for me as well. Thanks to @Labuan comment I also was able to make it work with FULLSCREEN theme placing that attribute on the ViewPager instead of the activity/fragment layout.Dulcet
Worked perfectly for me. I put it on the top of my layout in my fragment and now the fullscreen theme works fine with the adjustResize flag. Wonky.Octopod
H
8

I implemented Joseph Johnson solution and it worked well, I noticed after using this solution sometimes the drawer on the application will not close properly. I added a functionality to remove the listener removeOnGlobalLayoutListener when the user closes the fragment where are edittexts located.

    //when the application uses full screen theme and the keyboard is shown the content not scrollable! 
//with this util it will be scrollable once again
//https://mcmap.net/q/36361/-android-how-to-adjust-layout-in-full-screen-mode-when-softkeyboard-is-visible
public class AndroidBug5497Workaround {


    private static AndroidBug5497Workaround mInstance = null;
    private View mChildOfContent;
    private int usableHeightPrevious;
    private FrameLayout.LayoutParams frameLayoutParams;
    private ViewTreeObserver.OnGlobalLayoutListener _globalListener;

    // For more information, see https://code.google.com/p/android/issues/detail?id=5497
    // To use this class, simply invoke assistActivity() on an Activity that already has its content view set.

    public static AndroidBug5497Workaround getInstance (Activity activity) {
        if(mInstance==null)
        {
            synchronized (AndroidBug5497Workaround.class)
            {
                mInstance = new AndroidBug5497Workaround(activity);
            }
        }
        return mInstance;
    }

    private AndroidBug5497Workaround(Activity activity) {
        FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
        mChildOfContent = content.getChildAt(0);
        frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();

        _globalListener = new ViewTreeObserver.OnGlobalLayoutListener()
        {

            @Override
            public void onGlobalLayout()
            {
                 possiblyResizeChildOfContent();
            }
        };
    }

    public void setListener()
    {
         mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(_globalListener);
    }

    public void removeListener()
    {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            mChildOfContent.getViewTreeObserver().removeOnGlobalLayoutListener(_globalListener);
        } else {
            mChildOfContent.getViewTreeObserver().removeGlobalOnLayoutListener(_globalListener);
        }
    }

    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);
    } 
}

uses the class where is my edittexts located

@Override
public void onStart()
{
    super.onStart();
    AndroidBug5497Workaround.getInstance(getActivity()).setListener();
}

@Override
public void onStop()
{
    super.onStop();
    AndroidBug5497Workaround.getInstance(getActivity()).removeListener();
}
Hamer answered 13/4, 2016 at 14:16 Comment(0)
H
7

I'm currently using this approach and it works like a charm. The trick is we get keyboard height from different methods on 21 above and below and then use it as the bottom padding of our root view in our activity. I assumed your layout does not need a top padding (goes below status bar) but in case you do, inform me to update my answer.

MainActivity.java

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        RelativeLayout mainLayout = findViewById(R.id.main_layout);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ViewCompat.setOnApplyWindowInsetsListener(mainLayout , new OnApplyWindowInsetsListener() {
                @Override
                public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
                    v.setPadding(0, 0, 0, insets.getSystemWindowInsetBottom());
                    return insets;
                }
            });
        } else {
            View decorView = getWindow().getDecorView();
            final View contentView = mainLayout;
            decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    Rect r = new Rect();
                    //r will be populated with the coordinates of your view that area still visible.
                    decorView.getWindowVisibleDisplayFrame(r);

                    //get screen height and calculate the difference with the useable area from the r
                    int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels;
                    int diff = height - r.bottom;

                    //if it could be a keyboard add the padding to the view
                    if (diff != 0) {
                        // if the use-able screen height differs from the total screen height we assume that it shows a keyboard now
                        //check if the padding is 0 (if yes set the padding for the keyboard)
                        if (contentView.getPaddingBottom() != diff) {
                            //set the padding of the contentView for the keyboard
                            contentView.setPadding(0, 0, 0, diff);
                        }
                    } else {
                        //check if the padding is != 0 (if yes reset the padding)
                        if (contentView.getPaddingBottom() != 0) {
                            //reset the padding of the contentView
                            contentView.setPadding(0, 0, 0, 0);
                        }
                    }
                }
            });
        }
    }
...
}

Don't forget to address your root view with an id:

activity_main.xml

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

Hope it helps someone.

Huntington answered 3/10, 2018 at 8:26 Comment(1)
Can't understand, why this answer is still not in top. That others ones glitch, flash, but this one is brilliant, especially if you have 5+ api.Gibe
P
5

To get it to work with FullScreen:

Use the ionic keyboard plugin. This allows you to listen for when the keyboard appears and disappears.

OnDeviceReady add these event listeners:

// Allow Screen to Move Up when Keyboard is Present
window.addEventListener('native.keyboardshow', onKeyboardShow);
// Reset Screen after Keyboard hides
window.addEventListener('native.keyboardhide', onKeyboardHide);

The Logic:

function onKeyboardShow(e) {
    // Get Focused Element
    var thisElement = $(':focus');
    // Get input size
    var i = thisElement.height();
    // Get Window Height
    var h = $(window).height()
    // Get Keyboard Height
    var kH = e.keyboardHeight
    // Get Focused Element Top Offset
    var eH = thisElement.offset().top;
    // Top of Input should still be visible (30 = Fixed Header)
    var vS = h - kH;
    i = i > vS ? (vS - 30) : i;
    // Get Difference
    var diff = (vS - eH - i);
    if (diff < 0) {
        var parent = $('.myOuter-xs.myOuter-md');
        // Add Padding
        var marginTop = parseInt(parent.css('marginTop')) + diff - 25;
        parent.css('marginTop', marginTop + 'px');
    }
}

function onKeyboardHide(e) {
  // Remove All Style Attributes from Parent Div
  $('.myOuter-xs.myOuter-md').removeAttr('style');
}

Basically if they difference is minus then that is the amount of pixels that the keyboard is covering of your input. So if you adjust your parent div by this that should counteract it.

Adding timeouts to the logic say 300ms should also optimise performance (as this will allow keyboard time to appear.

Provoke answered 11/9, 2015 at 10:21 Comment(0)
B
5

1) Create KeyboardHeightHelper:

public class KeyboardHeightHelper {

    private final View decorView;
    private int lastKeyboardHeight = -1;

    public KeyboardHeightHelper(Activity activity, View activityRootView, OnKeyboardHeightChangeListener listener) {
        this.decorView = activity.getWindow().getDecorView();
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
            int keyboardHeight = getKeyboardHeight();
            if (lastKeyboardHeight != keyboardHeight) {
                lastKeyboardHeight = keyboardHeight;
                listener.onKeyboardHeightChange(keyboardHeight);
            }
        });
    }

    private int getKeyboardHeight() {
        Rect rect = new Rect();
        decorView.getWindowVisibleDisplayFrame(rect);
        return decorView.getHeight() - rect.bottom;
    }

    public interface OnKeyboardHeightChangeListener {
        void onKeyboardHeightChange(int keyboardHeight);
    }
}

2) Let your activity be full screen:

activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

3) Listen for keyboard height changes and add bottom padding for your view:

View rootView = activity.findViewById(R.id.root); // your root view or any other you want to resize
KeyboardHeightHelper effectiveHeightHelper = new KeyboardHeightHelper(
        activity, 
        rootView,
        keyboardHeight -> rootView.setPadding(0, 0, 0, keyboardHeight));

So, each time keyboard will appear on the screen - bottom padding for your view will change, and content will be rearranged.

Borrow answered 22/10, 2018 at 10:41 Comment(0)
A
4

I tried Joseph Johnson's class, and it worked, but didn't quite meet my needs. Rather than emulating android:windowSoftInputMode="adjustResize", I needed to emulate android:windowSoftInputMode="adjustPan".

I am using this for a full screen webview. To pan the content view to the correct position, I need to use a javascript interface which provides details on the position of the page element which has focus and thus is receiving the keyboard input. I have omitted those details, but provided my rewrite of Joseph Johnson's class. It will provide a very solid base for you to implement a custom pan vs. his resize.

package some.package.name;

import some.package.name.JavaScriptObject;

import android.app.Activity;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;

//-------------------------------------------------------
// ActivityPanner Class
//
// Convenience class to handle Activity attributes bug.
// Use this class instead of windowSoftInputMode="adjustPan".
//
// To implement, call enable() and pass a reference
// to an Activity which already has its content view set.
// Example:
//      setContentView( R.layout.someview );
//      ActivityPanner.enable( this );
//-------------------------------------------------------
//
// Notes:
//
// The standard method for handling screen panning
// when the virtual keyboard appears is to set an activity
// attribute in the manifest.
// Example:
// <activity
//      ...
//      android:windowSoftInputMode="adjustPan"
//      ... >
// Unfortunately, this is ignored when using the fullscreen attribute:
//      android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
//
//-------------------------------------------------------
public class ActivityPanner {

    private View contentView_;
    private int priorVisibleHeight_;

    public static void enable( Activity activity ) {
        new ActivityPanner( activity );
    }

    private ActivityPanner( Activity activity ) {
        FrameLayout content = (FrameLayout)
            activity.findViewById( android.R.id.content );
        contentView_ = content.getChildAt( 0 );
        contentView_.getViewTreeObserver().addOnGlobalLayoutListener(
            new ViewTreeObserver.OnGlobalLayoutListener() {
                public void onGlobalLayout() { panAsNeeded(); }
        });
    }

    private void panAsNeeded() {

        // Get current visible height
        int currentVisibleHeight = visibleHeight();

        // Determine if visible height changed
        if( currentVisibleHeight != priorVisibleHeight_ ) {

            // Determine if keyboard visiblity changed
            int screenHeight =
                contentView_.getRootView().getHeight();
            int coveredHeight =
                screenHeight - currentVisibleHeight;
            if( coveredHeight > (screenHeight/4) ) {
                // Keyboard probably just became visible

                // Get the current focus elements top & bottom
                // using a ratio to convert the values
                // to the native scale.
                float ratio = (float) screenHeight / viewPortHeight();
                int elTop = focusElementTop( ratio );
                int elBottom = focusElementBottom( ratio );

                // Determine the amount of the focus element covered
                // by the keyboard
                int elPixelsCovered = elBottom - currentVisibleHeight;

                // If any amount is covered
                if( elPixelsCovered > 0 ) {

                    // Pan by the amount of coverage
                    int panUpPixels = elPixelsCovered;

                    // Prevent panning so much the top of the element
                    // becomes hidden
                    panUpPixels = ( panUpPixels > elTop ?
                                    elTop : panUpPixels );

                    // Prevent panning more than the keyboard height
                    // (which produces an empty gap in the screen)
                    panUpPixels = ( panUpPixels > coveredHeight ?
                                    coveredHeight : panUpPixels );

                    // Pan up
                    contentView_.setY( -panUpPixels );
                }
            }
            else {
                // Keyboard probably just became hidden

                // Reset pan
                contentView_.setY( 0 );
            }

            // Save usabale height for the next comparison
            priorVisibleHeight_ = currentVisibleHeight;
        }
    }

    private int visibleHeight() {
        Rect r = new Rect();
        contentView_.getWindowVisibleDisplayFrame( r );
        return r.bottom - r.top;
    }

    // Customize this as needed...
    private int viewPortHeight() { return JavaScriptObject.viewPortHeight(); }
    private int focusElementTop( final float ratio ) {
        return (int) (ratio * JavaScriptObject.focusElementTop());
    }
    private int focusElementBottom( final float ratio ) {
        return (int) (ratio * JavaScriptObject.focusElementBottom());
    }

}
Admirable answered 5/8, 2014 at 20:50 Comment(2)
seems to be what i need, could you please add an complete sample? thanks for your work!Codycoe
I wasn't looking to post an entire project. What I've provided will bring you a very long way though towards a perfectly working solution. What you need to define yourself: Create a "JavaScriptObject" class and inject it into your webview as a js interface (Check the webview documentation for that). There's a good chance you've already done that if writing something that uses a webview in a comprehensive manner. Add JavaScript in the webview to listen for focus events and to feed data into your JavaScriptObject class about the focus element positioning.Admirable
S
2

Just keep as android:windowSoftInputMode="adjustResize". Because it is given to keep only one out of "adjustResize" and "adjustPan"(The window adjustment mode is specified with either adjustResize or adjustPan. It is highly recommended that you always specify one or the other). You can find it out here: http://developer.android.com/resources/articles/on-screen-inputs.html

It works perfectly for me.

Scofflaw answered 15/9, 2011 at 6:36 Comment(4)
I am not getting any problem...I have tried your XML also. This one also works..m using Os version 2.2Scofflaw
I have tried with full screen mode only...m testing it on my Nexus One and Nexus S....It works.Scofflaw
I have tried with Galaxy S, HTC wildfire, HTC Hero , Motorola Deify and Sony XPeria. Not working on any single device.Acquiesce
let us continue this discussion in chatScofflaw
A
2

Indeed the soft keyboard appearance doesn't seem to affect the Activity in any way no matter what windowSoftInputMode I select in the FullScreen mode.

Though I couldn't find much documentation on this property, I think that the FullScreen mode was designed for gaming application which do not require much use of the soft keyboard. If yours is an Activity which requires user interaction through soft keyboard, please reconsider using a non-FullScreen theme. You could turn off the TitleBar using a NoTitleBar theme. Why would you want to hide the notification bar?

Aura answered 26/9, 2011 at 4:50 Comment(0)
E
1

based on https://mcmap.net/q/36361/-android-how-to-adjust-layout-in-full-screen-mode-when-softkeyboard-is-visible and desire to make it happen...

updated idea


combining answers from

Relevant code:

        if (heightDifference > (usableHeightSansKeyboard / 4)) {

            // keyboard probably just became visible
            frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
        } else {

            // keyboard probably just became hidden
            if(usableHeightPrevious != 0) {
                frameLayoutParams.height = usableHeightSansKeyboard;
                activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
                activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

            }

Full Source at https://github.com/CrandellWS/AndroidBug5497Workaround/blob/master/AndroidBug5497Workaround.java

old idea

Create a static value of the containers height before opening the keyboard Set the container height based on usableHeightSansKeyboard - heightDifference when the keyboard opens and set it back to the saved value when it closes

if (heightDifference > (usableHeightSansKeyboard / 4)) {
                // keyboard probably just became visible
                frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
                int mStatusHeight = getStatusBarHeight();
                frameLayoutParams.topMargin = mStatusHeight;
                ((MainActivity)activity).setMyMainHeight(usableHeightSansKeyboard - heightDifference);

                if(BuildConfig.DEBUG){
                    Log.v("aBug5497", "keyboard probably just became visible");
                }
            } else {
                // keyboard probably just became hidden
                if(usableHeightPrevious != 0) {
                    frameLayoutParams.height = usableHeightSansKeyboard;
                    ((MainActivity)activity).setMyMainHeight();    
                }
                frameLayoutParams.topMargin = 0;

                if(BuildConfig.DEBUG){
                    Log.v("aBug5497", "keyboard probably just became hidden");
                }
            }

Methods in MainActivity

public void setMyMainHeight(final int myMainHeight) {

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            ConstraintLayout.LayoutParams rLparams =  (ConstraintLayout.LayoutParams) myContainer.getLayoutParams();
            rLparams.height = myMainHeight;

            myContainer.setLayoutParams(rLparams);
        }

    });

}

int mainHeight = 0;
public void setMyMainHeight() {

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            ConstraintLayout.LayoutParams rLparams =  (ConstraintLayout.LayoutParams) myContainer.getLayoutParams();
            rLparams.height = mainHeight;

            myContainer.setLayoutParams(rLparams);
        }

    });

}

Example Container XML

<android.support.constraint.ConstraintLayout
    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.support.constraint.ConstraintLayout
            android:id="@+id/my_container"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintHeight_percent=".8">

similarly margins can be added if needed...

Another consideration is use padding an example of this can be found at:

https://github.com/mikepenz/MaterialDrawer/issues/95#issuecomment-80519589

Exchequer answered 14/9, 2011 at 13:30 Comment(0)
B
1

only use android:windowSoftInputMode="adjustResize|stateHidden as you use AdjustPan then it disable the resizing property

Bifoliate answered 20/9, 2011 at 6:35 Comment(4)
I have used it too....please make you are doing it in full screen mode and on which device are you testing?Acquiesce
HTC NEXUS one, ok i hvnt add full screenBifoliate
can you use getWindow().requestFeature(Window.FEATURE_NO_TITLE); onCreate() instead using theme?Bifoliate
the above code is working fine without full screen but adding full screen either from xml or from code ... It is not working...Please read the question carefully.Acquiesce
P
1

I used Joseph Johnson created AndroidBug5497Workaround class but getting black space between softkeyboard and the view. I referred this link Greg Ennis. After doing some changes to the above this is my final working code.

 public class SignUpActivity extends Activity {

 private RelativeLayout rlRootView; // this is my root layout
 private View rootView;
 private ViewGroup contentContainer;
 private ViewTreeObserver viewTreeObserver;
 private ViewTreeObserver.OnGlobalLayoutListener listener;
 private Rect contentAreaOfWindowBounds = new Rect();
 private FrameLayout.LayoutParams rootViewLayout;
 private int usableHeightPrevious = 0;

 private View mDecorView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_sign_up);
  mDecorView = getWindow().getDecorView();
  contentContainer =
   (ViewGroup) this.findViewById(android.R.id.content);

  listener = new OnGlobalLayoutListener() {
   @Override
   public void onGlobalLayout() {
    possiblyResizeChildOfContent();
   }
  };

  rootView = contentContainer.getChildAt(0);
  rootViewLayout = (FrameLayout.LayoutParams)
  rootView.getLayoutParams();

  rlRootView = (RelativeLayout) findViewById(R.id.rlRootView);


  rlRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
   @Override
   public void onGlobalLayout() {
    int heightDiff = rlRootView.getRootView().getHeight() - rlRootView.getHeight();
    if (heightDiff > Util.dpToPx(SignUpActivity.this, 200)) {
     // if more than 200 dp, it's probably a keyboard...
     //  Logger.info("Soft Key Board ", "Key board is open");

    } else {
     Logger.info("Soft Key Board ", "Key board is CLOSED");

     hideSystemUI();
    }
   }
  });
 }

 // This snippet hides the system bars.
 protected void hideSystemUI() {
  // Set the IMMERSIVE flag.
  // Set the content to appear under the system bars so that the 
  content
  // doesn't resize when the system bars hide and show.
  mDecorView.setSystemUiVisibility(
   View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
 }
 @Override
 protected void onPause() {
  super.onPause();
  if (viewTreeObserver.isAlive()) {
   viewTreeObserver.removeOnGlobalLayoutListener(listener);
  }
 }

 @Override
 protected void onResume() {
  super.onResume();
  if (viewTreeObserver == null || !viewTreeObserver.isAlive()) {
   viewTreeObserver = rootView.getViewTreeObserver();
  }
  viewTreeObserver.addOnGlobalLayoutListener(listener);
 }

 @Override
 protected void onDestroy() {
  super.onDestroy();
  rootView = null;
  contentContainer = null;
  viewTreeObserver = null;
 }
 private void possiblyResizeChildOfContent() {
  contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds);

  int usableHeightNow = contentAreaOfWindowBounds.height();

  if (usableHeightNow != usableHeightPrevious) {
   rootViewLayout.height = usableHeightNow;
   rootView.layout(contentAreaOfWindowBounds.left,
    contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom);
   rootView.requestLayout();

   usableHeightPrevious = usableHeightNow;
  } else {

   this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
  }
 }
}
Priddy answered 29/5, 2017 at 12:2 Comment(0)
C
1
private void resizeWindowOnKeyboardVisible() {
            RelativeLayout rootLayout;
            rootLayout = findViewById(R.id.rootLayout);
            this.getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    
                ViewGroup.LayoutParams layoutParams = rootLayout.getLayoutParams();
                int height ;
    
                @Override
                public void onGlobalLayout() {
                    Rect r = new Rect();
                    getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
                    int screenHeight = rootLayout.getContext().getResources().getDisplayMetrics().heightPixels;
                    int heightDiff = screenHeight - r.bottom;
    
                    if (heightDiff > screenHeight*0.15)
                    {
                        height = screenHeight - heightDiff;
                        layoutParams.height=height;
                        rootLayout.setLayoutParams(layoutParams);
                    }else{
                        height=ViewGroup.LayoutParams.MATCH_PARENT;
                        if( height!=layoutParams.height) {
                            layoutParams.height = height;
                            rootLayout.setLayoutParams(layoutParams);
                        }
                    }
                }
            });
        }

Using android:windowSoftInputMode="adjustResize|stateHidden might not work in all cases and also android:fitsSystemWindows="true doesn't help when you use SYSTEM_UI_FLAG_FULLSCREEN tags. To make view/window/webview adjustable when Keyboard visible do the following things.

  • Use RelativeLayout as root layout.
  • Declare the above method resizeWindowOnKeyboardVisible() in an activity & call it after setContentView() in onCreate() method.

It works in Android 11 (API 30) also.

Cysticercoid answered 18/5, 2021 at 14:44 Comment(0)
B
1

Based on @Sdghasemi's solution, here's my Kotlin code, without the deprecated insets.getSystemWindowInsetBottom(). Also I added a padding animation to make the keyboard opening smoother.

val rootLayout = findViewById<RelativeLayout>(R.id.your_root_layout)      
ViewCompat.setOnApplyWindowInsetsListener(rootLayout) { v, insets ->
    val animator = ValueAnimator.ofInt(0, insets.getInsets(WindowInsetsCompat.Type.ime()).bottom))
    animator.addUpdateListener { 
        valueAnimator -> v.setPadding(0, 0, 0, valueAnimator.animatedValue as? Int ?: 0) 
    }
    animator.duration = 200
    animator.start()
    insets
}

Call it from the onCreate() method of your Activity. In my case, this snippet works better than setting android:windowSoftInputMode="adjustPan" in the AndroidManifest.xml

Barocchio answered 17/10, 2022 at 14:45 Comment(0)
H
0

You want the bottom bar to stick to the bottom of the view, but when the keyboard is displayed, they should move up to be placed above the keyboard, right?

You can try this code snippet:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    ...>

    <RelativeLayout
        android:id="@+id/RelativeLayoutTopBar"
    ...>
    </RelativeLayout>

    <LinearLayout
        android:id="@+id/LinearLayoutBottomBar"
        android:layout_alignParentBottom = true
        ...>
    </LinearLayout>

    <LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="390dp"
    android:orientation="vertical" 
    android:layout_above="@+id/LinearLayoutBottomBar"
    android:layout_below="@+id/RelativeLayoutTopBar"> 

    <ScrollView 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:id="@+id/ScrollViewBackground">

            ...

        </ScrollView>
     </LinearLayout>
  </RelativeLayout>

The BottomBar will stick to the bottom of the view and the LinearLayout containing the ScrollView will take what's left of the view after the top/bottom bar and the keyboard are displayed. Let me know if it works for you as well.

Housley answered 20/9, 2011 at 9:39 Comment(5)
Very strange as it worked in my apps several times. By the way, RelativeLayout has no orientation so you can delete these attributes in your code. I just recognized I could have cut down the code snippet to the line: android:layout_below="@+id/scoringContainerView" which you have to add to your ScrollViewHousley
Fullscreen? Do you mean without a layout at the top?Housley
no ..... I mean no status bar which show battery life, connectivity for the device etc....Acquiesce
no, the statusbar is visible in my app. can you try changing the order of your layouts, meaning that you put the code for your layout with the buttons above the other code and then try again? maybe you have to define them first in order to use the layout_belowHousley
please read the question carefully......I have mentioned I am having trouble with FullScreen mode......Acquiesce
P
0

Thank you Joseph for your answer. However, in the method possiblyResizeChildOfContent(), the portion

else {
            // keyboard probably just became hidden
            frameLayoutParams.height = usableHeightSansKeyboard;
        }

was not working for me, as the lower portion of view became hidden. So I had to take a global variable restoreHeight, and in the constructor, I inserted the last line

restoreHeight = frameLayoutParams.height;

and then I replaced the former mentioned part with

else {
            // keyboard probably just became hidden
            frameLayoutParams.height = restoreHeight;
        }

But I have no idea why your code didn't work for me. It would be of great help, if someone can shed light on this.

Persse answered 27/1, 2016 at 9:9 Comment(0)
A
0

I was only using full screen mode to hide the status bar. However, I want the app to resize when keyboard is shown. All of the other solutions (likely due to age of post) were complicated or not possible for my use (want to avoid change Java code for sack of PhoneGap Build).

Instead of using Full screen, I modified my configure for Android to be non-fullscreen:

            <preference name="fullscreen" value="false" />

And added the cordova-plugin-statusbar, via command line:

cordova plugin add cordova-plugin-statusbar

When app has loaded, I simple call a method on the plugin to hide itself, like:

    if (window.cordova && window.cordova.platformId == 'android' && window.StatusBar)
        window.StatusBar.hide();

This works like a charm. Only real downside is that the status bar is breifly visible while the app loads. For my needs, that wasn't an issue.

Applejack answered 25/3, 2016 at 19:14 Comment(0)
B
0

I have tried out all the possible answers from stackOverflow, finally i solved after a week Long search . I have used the coordinate layout and i changed this with linearLayout and my problem is fixed. I dont know possibly the coordinate layout has bugs or anything my mistake.

Bacteriolysis answered 26/6, 2016 at 15:23 Comment(0)
K
0

I tried many solutions include Joseph Johnson's and Johan Stuyts's. But as a result I got a white space between content and keyboard on some devices (like Lenovo s820) in all cases. So I made some changes to their codes and finally got working solution.

My idea based on adding margin to top of content when keyboard is showing.

contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds);
    int usableHeightNow = contentAreaOfWindowBounds.height();

    if (usableHeightNow != usableHeightPrevious) {

        int difference = usableHeightNow - usableHeightPrevious;

        if (difference < 0 && difference < -150) {
            keyboardShowed = true;
            rootViewLayout.topMargin -= difference + 30;
            rootViewLayout.bottomMargin += 30;
        }
        else if (difference < 0 && difference > -150){
            rootViewLayout.topMargin -= difference + 30;
        }
        else if (difference > 0 && difference > 150) {
            keyboardShowed = false;
            rootViewLayout.topMargin = 0;
            rootViewLayout.bottomMargin = 0;
        }

        rootView.requestLayout();

        Log.e("Bug Workaround", "Difference: " + difference);

        usableHeightPrevious = usableHeightNow;
}

As you can see, I add 30 px to difference because there is a small white space between top of the screen and content zone with margin. And I dont know whence it appears so I decided just make margins smaller and now it works exactly how I needed.

Kries answered 6/12, 2017 at 11:27 Comment(0)
P
0

Today not working adjustResize on full screen issue is actual for android sdk.

From answers i found:
the solution - but solution has this showing on picture issue :

Than i found the solution and remove the one unnecessary action:

this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

So, see my fixed solution code on Kotlin:

class AndroidBug5497Workaround constructor(val activity: Activity) {

    private val content = activity.findViewById<View>(android.R.id.content) as FrameLayout

    private val mChildOfContent = content.getChildAt(0)
    private var usableHeightPrevious: Int = 0
    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 listener = {
        possiblyResizeChildOfContent()
    }

    fun addListener() {
        mChildOfContent.apply {
            viewTreeObserver.addOnGlobalLayoutListener(listener)

        }
    }

    fun removeListener() {
        mChildOfContent.apply {
            viewTreeObserver.removeOnGlobalLayoutListener(listener)
        }
    }

    private fun possiblyResizeChildOfContent() {
        val contentAreaOfWindowBounds = Rect()
        mChildOfContent.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds)
        val usableHeightNow = contentAreaOfWindowBounds.height()

        if (usableHeightNow != usableHeightPrevious) {
            rootViewLayout.height = usableHeightNow
            rootView.layout(contentAreaOfWindowBounds.left,
                    contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom);
            mChildOfContent.requestLayout()
            usableHeightPrevious = usableHeightNow
        }
    }
}

My bug fixing implement code:

 class LeaveDetailActivity : BaseActivity(){

    private val keyBoardBugWorkaround by lazy {
        AndroidBug5497Workaround(this)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

    }

    override fun onResume() {
        keyBoardBugWorkaround.addListener()
        super.onResume()
    }

    override fun onPause() {
        keyBoardBugWorkaround.removeListener()
        super.onPause()
    }
}
Plutus answered 14/1, 2019 at 12:35 Comment(0)
M
0

There is another way, without creating own helper classes or functions that calculate the height of the screen. Instead use ViewCompat.setOnApplyWindowInsetsListener. With the listener you can check if the keyboard is open and set the bottom padding based on the keyboard height.

// the root view of your webview, e.g FrameLayout or LinearLayout
    rootView = view.findViewById(R.id.whatever);
    
    ViewCompat.setOnApplyWindowInsetsListener(rootView, (webView, insets) -> {
        
        // checks if keyboard is visible, the Type.ime() stands for Input Method
        boolean isKeyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
        
        // get the keyboard height and use the height as bottom padding for your view
        int bottomKeyboardPadding = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;

        
        if (isKeyboardVisible) { webView.setPadding(0, 0, 0, bottomKeyboardPadding); }
        else { webView.setPadding(0, 0, 0, 0); }

        return insets;
    });
Mauldon answered 7/2, 2022 at 8:12 Comment(0)
N
0

If you want to really support full screen with soft input:

private fun View.setStatusBarTransparent() {
    [email protected] {
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
        window.statusBarColor = ContextCompat.getColor(this, R.color.transparent)
        [email protected] = true
        WindowCompat.setDecorFitsSystemWindows(window, false)
        ViewCompat.setOnApplyWindowInsetsListener(this@setStatusBarTransparent) { root, windowInset ->
            val inset = windowInset.getInsets(WindowInsetsCompat.Type.systemBars())
            val inset2 = windowInset.getInsets(WindowInsetsCompat.Type.ime())
            root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
                leftMargin = inset.left
                bottomMargin = maxOf(inset.bottom, inset2.bottom)
                rightMargin = inset.right
            }
            WindowInsetsCompat.CONSUMED
        }
    }
}
Northing answered 4/8, 2022 at 8:58 Comment(1)
How to use ?...Rebarbative
G
-1

Don't use:

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

because works bad. Instead of that, use:

fun setFullScreen(fullScreen: Boolean) {
        val decorView = getWindow().getDecorView()
        val uiOptions : Int
        if(fullScreen){
            uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN // this hide statusBar
            toolbar.visibility = View.GONE // if you use toolbar
            tabs.visibility = View.GONE // if you use tabLayout
        } else {
            uiOptions = View.SYSTEM_UI_FLAG_VISIBLE // this show statusBar
            toolbar.visibility = View.VISIBLE
            tabs.visibility = View.VISIBLE
        }
        decorView.setSystemUiVisibility(uiOptions)
    }
Gammon answered 4/3, 2019 at 11:2 Comment(0)
H
-2

In my case, this issue started happening once I added Crosswalk to my Cordova application. My app is not used in fullscreen and android:windowSoftInputMode="adjustPan".

I already had the ionic keyboard plugin in the application, so detecting if the keyboard was up or down was easy thanks to it:

// Listen for events to when the keyboard is opened and closed
window.addEventListener("native.keyboardshow", keyboardUp, false);
window.addEventListener('native.keyboardhide', keyboardDown, false);

function keyboardUp()
{
    $('html').addClass('keyboardUp');
}

function keyboardDown()
{
    $('html').removeClass('keyboardUp');
}

I tried all of the fixes above but the simple line that ended up doing it for me was this bit of css:

&.keyboardUp {
        overflow-y: scroll;
}

Hope this saves you the few days I spent on this. :)

Headrail answered 6/7, 2016 at 22:12 Comment(2)
I'm using crosswalk with cordova as well with android:windowSoftInputMode="adjustPan". However, it's not working. I see that the class is getting added to the html element, but the css has no effect on the screen. Is there some other setting you have that allows for the screen to move?Beaulieu
I have to set the add transform:translateY(0px) for it to work. However, the scrolling doesn't work at all. Any ideas?Beaulieu

© 2022 - 2024 — McMap. All rights reserved.