当软键盘可见时,如何在全屏模式下调整布局

我已经研究了很多,以调整布局时,软键盘是活动的,我已经成功地实现了它,但问题来了,当我使用 android:theme="@android:style/Theme.NoTitleBar.Fullscreen"这在我的活动标记在清单文件。

对于这一点,我使用了 android:windowSoftInputMode="adjustPan|adjustResize|stateHidden"与不同的选项,但没有运气。

之后,我实现了 FullScreen的编程和尝试各种布局与 FullScreen的工作,但都是徒劳的。

我提到了这些链接,并在这里查看了许多与这个问题有关的帖子:

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 代码:

<?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>

< img src = “ https://i.stack.imgur.com/uAu3l.png”alt = “ enter image description here”> < img src = “ https://i.stack.imgur.com/uAu3l.png”alt = “ enter image description here”>

我想底部2个按钮应该向上,当软键盘进入图片。

enter image description here

177188 次浏览
保持 android:windowSoftInputMode="adjustResize"。因为它是给保持只有一个出 "adjustResize""adjustPan"(窗口调整模式是指定的调整大小或调整盘。强烈建议您始终指定其中之一)。你可以在这里找到: http://developer.android.com/resources/articles/on-screen-inputs.html

这对我来说再合适不过了。

只使用 android:windowSoftInputMode="adjustResize|stateHidden,因为你使用调整盘,然后它禁用调整大小属性

您希望底部的栏粘附在视图的底部,但是当显示键盘时,它们应该向上移动到键盘的上方,对吗?

您可以尝试下面的代码片段:

<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 将在显示顶部/底部栏和键盘后获取视图的剩余部分。如果对你也有效,请告诉我。

事实上,软键盘的外观似乎不会影响 Activity在任何方式,无论什么 windowSoftInputMode我选择在 FullScreen模式。

虽然我找不到太多关于这个属性的文档,但我认为 FullScreen模式是为游戏应用程序设计的,不需要太多软键盘的使用。如果您的活动需要用户通过软键盘交互,请重新考虑使用非全屏主题。您可以使用 NoTitleBar主题关闭 TitleBar。为什么要隐藏通知栏?

既然答案已经被选中,而且问题已知是一个 bug,我想我应该加上一个“可能的解决办法”。

当软键盘显示时,您可以切换全屏模式。这使得“调整盘”可以正常工作。

换句话说,我仍然使用 @ android: style/Theme. Black.NoTitleBar. Fullscreen 作为应用程序主题的一部分,使用 调整大小作为活动窗口软输入模式的一部分,但是为了让它们一起工作,我必须在键盘出现之前切换全屏模式。

使用以下代码:

关闭全屏模式

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

打开全屏模式

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

注释-灵感来自: 在全屏模式下隐藏标题

我也不得不面对这个问题,并有一个周围的工作,我检查了 HTC 1,银河系 s1,s2,s3,注意和 HTC 的感觉。

在布局的根视图上放置一个全局布局侦听器

mRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener(){
public void onGlobalLayout() {
checkHeightDifference();
}
});
在这里我检查了高度差,如果屏幕的高度差大于屏幕高度的三分之一,那么我们可以假设键盘是打开的。 从 这个答案拿来的
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;
}
}

这可能不是防弹的,也许在一些设备上它不会工作,但它为我工作,希望它也能帮助你。

基于 yghm 的解决方案,我编写了一个方便的类,它允许我用一行程序解决这个问题(当然是在将新类添加到源代码之后)。这句俏皮话是:

     AndroidBug5497Workaround.assistActivity(this);

实现类是:


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


希望这对谁有帮助。

我尝试了约瑟夫 · 约翰逊的课程,它起作用了,但不能完全满足我的需要。我需要模拟 android,而不是模拟 android: windowSoftInputMode = “ adjResize”。

我使用这个全屏网络视图。为了将内容视图平移到正确的位置,我需要使用一个 javascript 接口,该接口提供了页面元素的详细位置,该元素具有焦点,因此正在接收键盘输入。我已经省略了这些细节,但提供了我重写约瑟夫约翰逊的类。它将为您提供一个非常坚实的基础,以实现自定义平底锅相对于他的调整。

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


}

请注意,当 WindowManager.LayoutParams.FLAG_FULLSCREEN为某个活动设置时,android:windowSoftInputMode="adjustResize"不工作

  1. 为你的活动关闭全屏模式。活动不会在全屏模式下重新调整大小。您可以在 xml (通过更改活动的主题)或 Java 代码中执行此操作。在 onCreate ()方法中添加以下代码行。

    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.

要让它与全屏工作:

使用离子键盘插件。这允许你监听键盘出现和消失的时间。

OnDeviceReady 添加以下事件侦听器:

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

逻辑:

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毫秒也应该优化性能(因为这将允许键盘时间出现。

感谢 Joseph 的回答。然而,在方法可能 ResizeChildOfContent ()中,

else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightSansKeyboard;
}
对我来说不起作用,因为视图的下半部分被隐藏了起来。 因此,我必须使用一个全局变量 restoreHeight,并在构造函数中插入最后一行

restoreHeight = frameLayoutParams.height;

然后我把前面提到的部分替换成

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

但我不知道为什么你的代码对我不起作用。如果有人能解释清楚,那就帮大忙了。

我只是使用全屏模式来隐藏状态栏。但是,我希望应用程序调整大小时,键盘显示。所有其他的解决方案(可能由于邮政年龄)都很复杂,或者对我来说不可能使用(希望避免改变 Java 代码的 PhoneGap 构建的麻袋)。

我没有使用全屏,而是将 Android 的配置修改为非全屏:

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

并通过命令行添加了 cordova-plugin-statusbar:

cordova plugin add cordova-plugin-statusbar

当应用程序加载完成后,我简单地调用插件上的一个方法来隐藏自己,比如:

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

这招真管用。唯一真正的缺点是,在应用程序加载时,状态栏是可见的。对我来说,这不是问题。

我实现了约瑟夫约翰逊解决方案,它工作得很好,我注意到在使用这个解决方案后,有时应用程序上的抽屉不能正常关闭。 我添加了一个功能,当用户关闭编辑文本所在的片段时,删除侦听器 RemoveOnGlobalLayoutListener
    //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);
}
}

使用我的编辑文本所在的类

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


@Override
public void onStop()
{
super.onStop();
AndroidBug5497Workaround.getInstance(getActivity()).removeListener();
}
我已经尝试了 stackOverflow 中所有可能的答案,经过一个星期的搜索,我终于找到了答案。 我已经使用了坐标布局和我改变了线性布局和我的问题是固定的。我不知道可能坐标布局有错误或任何我的错误。译注:

在我的例子中,当我将 Crosswalk 添加到 Cordova 应用程序中时,这个问题就开始发生了。我的应用程序不在全屏和安卓系统中使用:。

我已经在应用程序中安装了离子键盘插件,因此检测键盘是向上还是向下很容易,这要归功于它:

// 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:

&.keyboardUp {
overflow-y: scroll;
}

希望这能帮你省下我花在这上面的几天时间。 :)

我尝试了 约瑟夫 · 约翰逊的解决方案,但是和其他人一样,我也遇到了内容和键盘之间的差距问题。出现这个问题是因为在使用全屏模式时,软输入模式总是 。当您激活一个被软输入隐藏的输入域时,这种平移会干扰 Joseph 的解决方案。

当软输入出现时,内容首先基于其原始高度进行平移,然后根据 Joseph 解决方案请求的布局调整大小。调整大小和随后的布局不会撤消平移,从而导致间隙。整个事件的顺序是:

  1. 全局布局监听器
  2. 平移
  3. 内容的布局(= 内容的实际大小调整)

不可能禁用平移,但可以通过更改内容的高度来强制平移偏移量为0。这可以在侦听器中完成,因为它在进行平移之前运行。将内容高度设置为可用高度可以获得平滑的用户体验,即没有闪烁。

我还做了一些修改,如果有任何引入的问题,请告诉我:

  • 将可用高度的确定切换为使用 getWindowVisibleDisplayFrameRect被缓存以防止一点不需要的垃圾。
  • 也允许删除侦听器。当您为具有不同全屏需求的不同片段重用活动时,这非常有用。
  • 不要区分显示或隐藏键盘,但始终将内容高度设置为可见显示框高度。

它已经在 Nexus5上进行了测试,模拟器运行的 API 级别为16-24,屏幕尺寸从小到大不等。

代码已经被移植到了 Kotlin,但是将我的修改移植回 Java 很简单。如果你需要帮助,请告诉我:

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) ,我刚刚发现了一个简单而可靠的解决方案。

它在使用 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN的情况下工作,例如,如果使用 CoordinatorLayout

它不适用于 WindowManager.LayoutParams.FLAG_FULLSCREEN(你也可以用 android:windowFullscreen设置主题) ,但是你可以用 SYSTEM_UI_FLAG_LAYOUT_STABLE达到类似的效果(它具有相同的视觉效果) ,这个解决方案应该可以再次工作。

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)

我在运行棉花糖的设备上测试过。

关键是软键盘也是系统窗口之一(如状态栏和导航栏) ,所以系统发送的 WindowInsets包含了准确可靠的信息。

对于像 DrawerLayout这样的用例,我们试图在状态栏后面绘制,我们可以创建一个布局,只忽略顶部的插入,并应用底部的插入来解决软键盘的问题。

这是我的定制 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);
}
}
}

使用它:

<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>

这在理论上应该适用于任何设备而不需要疯狂的修改,比任何试图以屏幕大小的随机 1/31/4作为参考的黑客攻击要好得多。

(它需要 API 16 + ,但是我只在 Lollipop + 上使用全屏来在状态栏后面绘图,所以它是这种情况下的最佳解决方案。)

我使用 Joseph Johnson 创建了 AndroidBug5497Workaround 类,但是在软键盘和视图之间获得了黑色空间。我提到这个链接 Greg Ennis。在对上面做了一些修改之后,这是我最后的工作代码。

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

android:fitsSystemWindows="true"添加到布局中,此布局将调整大小。

我尝试了很多解决方案,包括约瑟夫 · 约翰逊和约翰 · 斯图茨的。但结果是,在某些设备(比如联想 s820)的内容和键盘之间总是留有空白。 所以我对他们的代码做了一些修改,最终得到了可行的解决方案

我的想法基于在键盘显示时在内容顶部添加页边距。

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像素的差异,因为有一个小的空白之间的屏幕顶部和内容区与边距。我不知道它从哪里出现,所以我决定只是使利润率更小,现在它的工作正是我所需要的。

我目前正在使用这种方法,效果非常好。诀窍是我们从上面和下面的21个不同的方法中获得 键盘高度,然后在我们的活动中使用它作为根视图的底部填充。我假设你的布局不需要一个顶部填充(去下面的状态栏) ,但如果你这样做,通知我更新我的答案。

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

不要忘记使用 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">

希望能帮到别人。

基于 https://stackoverflow.com/a/19494006/1815624并渴望实现它..。

更新的想法


把... 的答案结合起来

相关守则:

        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的完整来源

旧主意

在打开键盘之前创建容器高度的静态值 当键盘打开时,根据 usableHeightSansKeyboard - heightDifference设置容器高度,并在关闭

时将其设置回保存的值
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");
}
}

主要活动方法

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

<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

1)创建键盘高度助手:

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)让你的活动全屏显示:

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

3)监听键盘高度的变化,并为视图添加底部填充:

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

那么,每次键盘将出现在屏幕底部填充为您的视图将改变,并重新安排内容。

今天不工作调整大小全屏幕问题是实际的 android sdk。

我从答案中发现: 但是解决方案在图片问题上有这样的表现: < br >

比我发现 解决方案和删除一个不必要的行动:

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

所以,看看我在 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
}
}
}

我的 bug 修复实现代码:

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

不要使用:

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
因为工作很糟糕。 使用:

代替它
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)
}
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);
}
}
}
});
}

使用 android: windowSoftInputMode = “ adjResize | stateHidden 可能无法在所有情况下工作,而且 android: fitsSystemWindows = “ true 在使用 SYSTEM _ UI _ FLAG _ FULLSCREEN 标签时没有帮助。要使视图/窗口/webview 在可见键盘时可调整,请执行下列操作。

  • 使用 RelativeLayout 作为根布局。
  • 在活动中声明上面的方法 resizeWindowOnKeyboardVisible () ,并在 onCreate ()方法中的 setContentView ()之后调用它。

它也适用于 Android 11(API 30)。

还有另一种方法,不用创建自己的辅助类或函数来计算屏幕的高度。而是使用 ViewCompat.setOnApplyWindowInsetsListener。 通过侦听器,您可以检查键盘是否打开,并根据键盘高度设置底部填充。译注:
// 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;
});

如果你想真正支持全屏软输入:

private fun View.setStatusBarTransparent() {
this@MainActivity.apply {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.statusBarColor = ContextCompat.getColor(this, R.color.transparent)
this@setStatusBarTransparent.fitsSystemWindows = 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
}
}
}

基于@Sdghasemi 的解决方案,这是我的 Kotlin 代码,没有弃用的 insets.getSystemWindowInsetBottom()。此外,我添加了填充动画,使键盘打开顺利。

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
}
ActivityonCreate()方法调用它。 在我的例子中,这个代码片段比在 AndroidManifest.xml

中设置 android:windowSoftInputMode="adjustPan"更好