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

前端 未结 25 1774
后悔当初
后悔当初 2020-11-22 03:56

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=\"@andro

相关标签:
25条回答
  • 2020-11-22 04:30

    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.

    0 讨论(0)
  • 2020-11-22 04:32

    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.

    0 讨论(0)
  • 2020-11-22 04:33

    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.

    0 讨论(0)
  • 2020-11-22 04:33

    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.

    0 讨论(0)
  • 2020-11-22 04:33

    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());
        }
    
    }
    
    0 讨论(0)
  • 2020-11-22 04:34

    based on https://stackoverflow.com/a/19494006/1815624 and desire to make it happen...

    updated idea


    combining answers from

    • https://stackoverflow.com/a/19494006/1815624
    • https://stackoverflow.com/a/10952394/1815624

    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

    0 讨论(0)
提交回复
热议问题