Android How to adjust layout in Full Screen Mode when softkeyboard is visible
当软键盘处于活动状态并且我已经成功实现它时,我已经研究了很多调整布局但是当我在清单文件中的活动标签中使用
为此,我使用了
之后我以编程方式实现了
我引用了这些链接,并在此处查看了许多与此问题相关的帖子:
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/
这是xml代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | <?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> |
我希望当软键盘出现时,底部的2个按钮应该向上。
根据yghm的解决方法,我编写了一个便利类,允许我用一行代码解决问题(当然,在将新类添加到我的源代码之后)。单线是:
1 | AndroidBug5497Workaround.assistActivity(this); |
实现类是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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); } } |
希望这有助于某人。
http://code.google.com/p/android/issues/list?can=2&q=fullscreen&colspec=ID+Type+Status+Owner+Summary+Stars&cells=tiles
- > http://code.google.com/p/android/issues/detail?id=5497&q=fullscreen&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars
- > https://groups.google.com/group/android-developers/msg/5690ac3a9819a53b?pli=1
- >全屏模式不调整大小
由于答案已经被挑选并且已知问题是一个错误,我想我会添加一个"可能的工作"。
显示软键盘时,您可以切换全屏模式。这允许"adjustPan"正常工作。
换句话说,我仍然使用@android:style / Theme.Black.NoTitleBar.Fullscreen作为应用程序主题的一部分,并将stateVisible | adjustResize作为活动窗口软输入模式的一部分但是要让它们一起工作我必须切换全屏模式在键盘出现之前。
使用以下代码:
关闭全屏模式
1 2 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); |
打开全屏模式
1 2 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); |
注意 - 灵感来自:在全屏模式下隐藏标题
我尝试了约瑟夫约翰逊的解决方案,但与其他人一样,我遇到了内容与键盘之间的差距问题。出现此问题的原因是使用全屏模式时软输入模式始终为平移。当您激活将被软输入隐藏的输入字段时,此平移会干扰Joseph的解决方案。
当软输入出现时,首先根据其原始高度平移内容,然后按照Joseph解决方案请求的布局调整大小。调整大小和后续布局不会撤消平移,这会导致间隙。事件的完整顺序是:
无法禁用平移,但可以通过更改内容的高度来强制平移偏移为0。这可以在侦听器中完成,因为它在平移发生之前运行。将内容高度设置为可用高度导致平滑的用户体验,即没有闪烁。
我也做了这些改变。如果其中任何一个引入问题,请告诉我:
-
切换确定可用高度
getWindowVisibleDisplayFrame 。缓存Rect 以防止一点不需要的垃圾。 - 也允许删除侦听器。当您为具有不同全屏要求的不同片段重用活动时,这非常有用。
- 不要区分显示或隐藏的键盘,但始终将内容高度设置为可见的显示帧高度。
它已经在Nexus 5上进行了测试,模拟器运行的API级别为16-24,屏幕尺寸从微小到大。
代码已移植到Kotlin,但将我的更改移植回Java很简单。如果您需要帮助,请告诉我们:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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 } } } |
如果您使用系统UI方法(https://developer.android.com/training/system-ui/immersive.html),我刚刚找到了一个简单可靠的解决方案。
它适用于使用
它不适用于
1 2 3 | 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) |
我在运行Marshmallow的设备上测试过它。
关键是软键盘也是系统窗口之一(如状态栏和导航栏),因此系统发送的
对于
这是我的自定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /** * 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); } } } |
并使用它:
1 2 3 4 5 6 7 | <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> |
理论上,这应该适用于任何没有疯狂修改的设备,比任何试图采用随机
(它需要API 16+,但我只在Lollipop +上使用全屏来绘制状态栏,因此在这种情况下它是最佳解决方案。)
我也不得不面对这个问题并且有一个工作我在HTC one,galaxy s1,s2,s3,note和HTC感觉上检查。
在布局的根视图上放置一个全局布局侦听器
1 2 3 4 5 | mRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener(){ public void onGlobalLayout() { checkHeightDifference(); } }); |
在那里我检查了高度差,如果屏幕的高度差大于屏幕高度的三分之一,那么我们可以假设键盘是打开的。
从这个答案中得到了它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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; } } |
这可能不是防弹,也许在某些设备上它不起作用,但它对我有用,并希望它也会帮助你。
请注意,当为活动设置
为您的活动禁用全屏模式。在全屏模式下不会调整活动的大小。您可以在xml中(通过更改活动的主题)或在Java代码中执行此操作。在onCreate()方法中添加以下行。
1 2 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);` |
要么
使用另一种方法来实现全屏模式。在onCreate()方法中添加以下代码。
1 2 3 4 5 6 7 | 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);` |
请注意,方法2仅适用于Android 4.1及更高版本。
我实施了Joseph Johnson解决方案并且运行良好,我注意到在使用此解决方案后,应用程序上的抽屉有时无法正常关闭。
我添加了一个功能,当用户关闭edittexts所在的片段时,删除侦听器removeOnGlobalLayoutListener。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | //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 //http://stackoverflow.com/questions/7417123/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); } } |
使用我的edittexts所在的类
1 2 3 4 5 6 7 8 9 10 11 12 13 | @Override public void onStart() { super.onStart(); AndroidBug5497Workaround.getInstance(getActivity()).setListener(); } @Override public void onStop() { super.onStop(); AndroidBug5497Workaround.getInstance(getActivity()).removeListener(); } |
将
要使其与FullScreen一起使用:
使用离子键盘插件。这使您可以在键盘出现和消失时进行监听。
OnDeviceReady添加这些事件侦听器:
1 2 3 4 | // Allow Screen to Move Up when Keyboard is Present window.addEventListener('native.keyboardshow', onKeyboardShow); // Reset Screen after Keyboard hides window.addEventListener('native.keyboardhide', onKeyboardHide); |
逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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'); } |
基本上,如果它们的差异为负,则表示键盘覆盖输入的像素数量。因此,如果你调整你的父div,应该抵消它。
向逻辑添加超时300毫秒也应该优化性能(因为这将允许键盘时间出现。
我尝试了约瑟夫约翰逊的课程,虽然有效,但并不能满足我的需求。而不是模仿android:windowSoftInputMode ="adjustResize",我需要模拟android:windowSoftInputMode ="adjustPan"。
我正在使用它来进行全屏webview。要将内容视图平移到正确的位置,我需要使用一个javascript界面??,它提供有焦点的页面元素位置的详细信息,从而接收键盘输入。我已经省略了这些细节,但是我重写了约瑟夫约翰逊的课程。它将为您提供一个非常可靠的基础,以实现自定义泛与其调整大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | 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()); } } |
保持
http://developer.android.com/resources/articles/on-screen-inputs.html
它对我来说很完美。
实际上,无论我在
虽然我找不到很多关于这个属性的文档,但我认为
我目前正在使用这种方法,它就像一个魅力。诀窍是我们从上面和下面21的不同方法获得键盘高度,然后在我们的活动中将它用作我们根视图的底部填充。我假设您的布局不需要顶部填充(在状态栏下方),但如果您这样做,请通知我更新我的答案。
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 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); } } } }); } } ... } |
不要忘记使用id来解决根视图:
activity_main.xml中
1 2 3 4 5 | <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"> |
希望它可以帮助某人。
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | 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); } } } |
基于https://stackoverflow.com/a/19494006/1815624并希望实现这一目标......
更新的想法
结合答案
- https://stackoverflow.com/a/19494006/1815624
- https://stackoverflow.com/a/10952394/1815624
相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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); } |
完整资料来源:https://github.com/CrandellWS/AndroidBug5497Workaround/blob/master/AndroidBug5497Workaround.java
老想法
在打开键盘之前创建容器高度的静态值
键盘打开时根据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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"); } } |
MainActivity中的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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); } }); } |
示例容器XML
1 2 3 4 5 6 7 8 9 10 11 | <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"> |
如果需要,可以添加类似的利润......
另一个考虑因素是使用填充,可以在以下位置找到此示例:
https://github.com/mikepenz/MaterialDrawer/issues/95#issuecomment-80519589
在使用AdjustPan时仅使用
您希望底栏粘在视图的底部,但是当显示键盘时,它们应向上移动以放置在键盘上方,对吧?
您可以尝试以下代码段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <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> |
BottomBar将粘贴到视图的底部,包含ScrollView的LinearLayout将在顶部/底部栏和键盘显示后占据视图的左侧。如果它对您有用,请告诉我。
我只是使用全屏模式来隐藏状态栏。但是,我希望应用程序在显示键盘时调整大小。所有其他解决方案(可能由于帖子的年龄)都很复杂或不可能(我希望避免更改Java代码以解决PhoneGap Build的问题)。
我没有使用全屏,而是将我的Android配置修改为非全屏:
1 | [cc] |
并通过命令行添加
1 | cordova plugin add cordova-plugin-statusbar |
当应用程序加载后,我简单地调用插件上的方法来隐藏自己,如:
1 2 | if (window.cordova && window.cordova.platformId == 'android' && window.StatusBar) window.StatusBar.hide(); |
这就像一个魅力。唯一真正的缺点是,当应用程序加载时,状态栏是明显可见的。根据我的需要,这不是问题。
不要使用:
1 2 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); |
因为工作不好。
而不是那样,使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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) } |
我已经尝试了stackOverflow的所有可能的答案,最后我在一周的长搜索后解决了。
我使用了坐标布局,我用linearLayout更改了它,我的问题得到了解决。我不知道坐标布局可能有错误或任何我的错误。
我尝试了很多解决方案,包括Joseph Johnson和Johan Stuyts。但结果是,在所有情况下,我在某些设备(如联想s820)上的内容和键盘之间都有一个空白区域。
所以我对他们的代码做了一些修改,最后得到了解决方案。
我的想法是基于在键盘显示时为内容添加边距。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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; } |
正如您所看到的,我添加了30 px差异,因为屏幕顶部和内容区域之间有一个小的空白区域。而且我不知道它从哪里出现所以我决定将边距缩小,现在它正是我需要的。
1)创建KeyboardHeightHelper:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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)让您的活动全屏:
3)监听键盘高度变化并为视图添加底部填充:
1 2 3 4 5 | 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)); |
因此,每次键盘都会出现在屏幕上 - 视图的底部填充将发生变化,内容将重新排列。
今天没有工作adjustResize全屏问题是实际的Android sdk。
从我找到的答案:
解决方案 - 但解决方案显示图片问题:
1 | this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); |
所以,请参阅我在Kotlin上的固定解决方案代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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 } } } |
我的bug修复工具代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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() } } |
谢谢约瑟夫的回答。但是,在方法possibleResizeChildOfContent()中,该部分
1 2 3 4 | else { // keyboard probably just became hidden frameLayoutParams.height = usableHeightSansKeyboard; } |
因为视图的下半部分被隐藏起来,所以对我不起作用。
所以我不得不采用全局变量restoreHeight,并在构造函数中插入最后一行
1 | restoreHeight = frameLayoutParams.height; |
然后我替换了前面提到的部分
1 2 3 4 | else { // keyboard probably just became hidden frameLayoutParams.height = restoreHeight; } |
但我不知道为什么你的代码不适合我。如果有人能够阐明这一点,那将会有很大的帮助。
就我而言,一旦我将Crosswalk添加到我的Cordova应用程序中,这个问题就开始发生了。我的应用程序未在全屏和android中使用:windowSoftInputMode ="adjustPan"。
我已经在应用程序中使用了离子键盘插件,因此检测键盘是上升还是下降很容易归功于它:
1 2 3 4 5 6 7 8 9 10 11 12 13 | // 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'); } |
我尝试了上面的所有修复,但最终为我做的简单线是这个css:
1 2 3 | &.keyboardUp { overflow-y: scroll; } |
希望这能节省你花在这上面的几天。 :)