windowSoftInputMode =“adjustResize”不适用于半透明动作/导航栏

我在新的Android KitKat(4.4)和windowSoftInputMode="adjustResize"有半透明的actionbar / navbar问题。

通常将InputMode更改为adjustResize,应用程序应该在显示键盘时自行resize…但是在这里它不会! 如果我删除透明效果的行,resize工作。

所以如果键盘是可见的,我的ListView是在它下面,我不能访问最后几个项目。 (只能通过手动隐藏键盘)

AndroidManifest.xml中

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="XYZ" android:versionCode="23" android:versionName="0.1" > <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/Theme.XYZStyle" > <activity android:name="XYZ" android:label="@string/app_name" android:windowSoftInputMode="adjustResize" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> 

值-V19 / styles.xml

 <?xml version="1.0" encoding="utf-8"?> <resources> <style name="Theme.XYZStyle" parent="@style/Theme.AppCompat.Light"> <item name="android:windowTranslucentStatus">true</item> <item name="android:windowTranslucentNavigation">true</item> </style> </resources> 

fragment.xml之

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ListView android:id="@+id/listView_contacts" android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false" android:divider="@null" android:dividerHeight="0dp" android:drawSelectorOnTop="true" android:fastScrollAlwaysVisible="true" android:fastScrollEnabled="true" android:paddingBottom="@dimen/navigationbar__height" > </ListView> </RelativeLayout> 

任何想法来解决这个问题?

您缺less以下属性:

 android:fitsSystemWindows="true" 

在fragment.xml布局的根RelativeLayout中(或者可能在活动布局上,不确定,因为我们没有完整的应用程序结构)

这里有一个相关的错误报告。 我发现一个解决方法,从有限的testing,似乎是做的伎俩,没有影响。 添加一个自定义的根ViewGroup (我几乎总是使用FrameLayout ,所以这是我testing过的)与下面的逻辑。 然后,使用此自定义布局来代替您的根布局,并确保您设置了android:fitsSystemWindows="true" 。 您可以随时调用getInsets()布局之后(例如,添加一个OnPreDrawListener )来调整布局的其余部分以适应系统插入(如果需要)。

 import android.content.Context; import android.graphics.Rect; import android.os.Build; import android.util.AttributeSet; import android.widget.FrameLayout; import org.jetbrains.annotations.NotNull; /** * @author Kevin * Date Created: 3/7/14 * * https://code.google.com/p/android/issues/detail?id=63777 * * When using a translucent status bar on API 19+, the window will not * resize to make room for input methods (ie * {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE} and * {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_PAN} are * ignored). * * To work around this; override {@link #fitSystemWindows(android.graphics.Rect)}, * capture and override the system insets, and then call through to FrameLayout's * implementation. * * For reasons yet unknown, modifying the bottom inset causes this workaround to * fail. Modifying the top, left, and right insets works as expected. */ public final class CustomInsetsFrameLayout extends FrameLayout { private int[] mInsets = new int[4]; public CustomInsetsFrameLayout(Context context) { super(context); } public CustomInsetsFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); } public CustomInsetsFrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public final int[] getInsets() { return mInsets; } @Override protected final boolean fitSystemWindows(@NotNull Rect insets) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // Intentionally do not modify the bottom inset. For some reason, // if the bottom inset is modified, window resizing stops working. // TODO: Figure out why. mInsets[0] = insets.left; mInsets[1] = insets.top; mInsets[2] = insets.right; insets.left = 0; insets.top = 0; insets.right = 0; } return super.fitSystemWindows(insets); } } 

由于fitSystemWindow已被弃用,请参考下面的答案来完成解决方法。

@kcoppock答案确实有帮助,但fitSystemWindows在API级别20已被弃用

因此,由于API 20(KITKAT_WATCH),你应该重写onApplyWindowInsets

 @Override public final WindowInsets onApplyWindowInsets(WindowInsets insets) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { return super.onApplyWindowInsets(insets.replaceSystemWindowInsets(0, 0, 0, insets.getSystemWindowInsetBottom())); } else { return insets; } } 

这对我来说有半透明的状态栏和adjustResize在片段:

  1. 做一个自定义的RelativeLayout作为@ Victor91和@ kcoppock说。

  2. 使用CustomRelativeLayout作为您的片段的父布局。

  3. 用android声明主题:windowTranslucentStatus = true

  4. 容器Activity必须在Manifest中用android:windowSoftInputMode =“adjustResize”声明并使用声明的主题

  5. 请在碎片根部布局上使用fitsSystemWindows!

     public class CustomRelativeLayout extends RelativeLayout { private int[] mInsets = new int[4]; public CustomRelativeLayout(Context context) { super(context); } public CustomRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); } public CustomRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public CustomRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public final WindowInsets onApplyWindowInsets(WindowInsets insets) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { mInsets[0] = insets.getSystemWindowInsetLeft(); mInsets[1] = insets.getSystemWindowInsetTop(); mInsets[2] = insets.getSystemWindowInsetRight(); return super.onApplyWindowInsets(insets.replaceSystemWindowInsets(0, 0, 0, insets.getSystemWindowInsetBottom())); } else { return insets; } } } 

然后在xml中,

 <com.blah.blah.CustomRelativeLayout 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:fitsSystemWindows="true"> </com.blah.blah.CustomRelativeLayout> 

我有同样的问题,我的活动有一个ScrollView作为根视图和半透明状态栏激活它没有正确调整时,键盘显示…并连续屏幕没有滚动隐藏input的意见。

解决scheme:将所有内容(布局和活动逻辑)都移到一个新的片段中。 然后改变活动只包含这个片段。 现在一切都按预期工作!

这是活动的布局:

 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/contentView" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" /> 

基于约瑟夫·约翰逊在Android的解决方法如何在全屏模式下调整布局,当软键盘是可见的

在你的activity中的setContentView()之后调用onCreate()

AndroidBug5497Workaround.assistActivity(this);

一个不同于原来的替代return (r.bottom - r.top);return r.bottom;computeUsableHeight()

出于某种原因,我必须将我的活动fitsSystemWindows属性设置为false

这个解决方法救了我。 这对我来说很好。 希望能帮助你。

实现类是:

 public class AndroidBug5497Workaround { // 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 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; } } 

AndroidBug5497Workaround.java注意内存泄漏。 需要下面的代码

 getViewTreeObserver().removeOnGlobalLayoutListener(listener); 

我使用RxJava的示例,当Activity的生命周期中的onPause()时自动调用removeOnGlobalLayoutListener()

 public class MyActivity extends RxAppCompatActivity { // ... protected void onStart(){ super.onStart(); TRSoftKeyboardVisibility .changes(this) // activity .compose(this.<TRSoftKeyboardVisibility.ChangeEvent>bindUntilEvent(ActivityEvent.PAUSE)) .subscribe(keyboardEvent -> { FrameLayout content = (FrameLayout) findViewById(android.R.id.content); View firstChildView = content.getChildAt(0); firstChildView.getLayoutParams().height = keyboardEvent.viewHeight(); firstChildView.requestLayout(); // keyboardEvent.isVisible = keyboard visible or not // keyboardEvent.keyboardHeight = keyboard height // keyboardEvent.viewHeight = fullWindowHeight - keyboardHeight }); //... } package commonlib.rxjava.keyboard; import android.app.Activity; import android.view.View; import android.widget.FrameLayout; import kr.ohlab.android.util.Assert; import rx.Observable; public class TRSoftKeyboardVisibility { public static Observable<ChangeEvent> changes(Activity activity) { Assert.notNull(activity, "activity == null"); FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content); View childOfContent = content.getChildAt(0); return Observable.create( new TRSoftKeyboardVisibilityEventOnSubscribe(childOfContent)); } public static final class ChangeEvent { private final int keyboardHeight; private final boolean visible; private final int viewHeight; public static ChangeEvent create(boolean visible, int keyboardHeight, int windowDisplayHeight) { return new ChangeEvent(visible, keyboardHeight, windowDisplayHeight); } private ChangeEvent(boolean visible, int keyboardHeight, int viewHeight) { this.keyboardHeight = keyboardHeight; this.visible = visible; this.viewHeight = viewHeight; } public int keyboardHeight() { return keyboardHeight; } public boolean isVisible() { return this.visible; } public int viewHeight() { return viewHeight; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ChangeEvent)) return false; ChangeEvent that = (ChangeEvent) o; if (keyboardHeight != that.keyboardHeight) return false; if (visible != that.visible) return false; return viewHeight == that.viewHeight; } @Override public int hashCode() { int result = keyboardHeight; result = 31 * result + (visible ? 1 : 0); result = 31 * result + viewHeight; return result; } @Override public String toString() { return "ChangeEvent{" + "keyboardHeight=" + keyboardHeight + ", visible=" + visible + ", viewHeight=" + viewHeight + '}'; } } } package commonlib.rxjava.keyboard; import android.graphics.Rect; import android.view.View; import android.view.ViewTreeObserver; import kr.ohlab.android.util.Assert; import rx.Observable; import rx.Subscriber; import rx.android.MainThreadSubscription; import timber.log.Timber; public class TRSoftKeyboardVisibilityEventOnSubscribe implements Observable.OnSubscribe<TRSoftKeyboardVisibility.ChangeEvent> { private final View mTopView; private int mLastVisibleDecorViewHeight; private final Rect mWindowVisibleDisplayFrame = new Rect(); public TRSoftKeyboardVisibilityEventOnSubscribe(View topView) { mTopView = topView; } private int computeWindowFrameHeight() { mTopView.getWindowVisibleDisplayFrame(mWindowVisibleDisplayFrame); return (mWindowVisibleDisplayFrame.bottom - mWindowVisibleDisplayFrame.top); } private TRSoftKeyboardVisibility.ChangeEvent checkKeyboardVisibility() { int windowFrameHeightNow = computeWindowFrameHeight(); TRSoftKeyboardVisibility.ChangeEvent event = null; if (windowFrameHeightNow != mLastVisibleDecorViewHeight) { int mTopViewHeight = mTopView.getHeight(); int heightDiff = mTopViewHeight - windowFrameHeightNow; Timber.e("XXX heightDiff=" + heightDiff); if (heightDiff > (mTopViewHeight / 4)) { event = TRSoftKeyboardVisibility.ChangeEvent.create(true, heightDiff, windowFrameHeightNow); } else { event = TRSoftKeyboardVisibility.ChangeEvent.create(false, 0, windowFrameHeightNow); } mLastVisibleDecorViewHeight = windowFrameHeightNow; return event; } return null; } public void call(final Subscriber<? super TRSoftKeyboardVisibility.ChangeEvent> subscriber) { Assert.checkUiThread(); final ViewTreeObserver.OnGlobalLayoutListener listener = new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { TRSoftKeyboardVisibility.ChangeEvent event = checkKeyboardVisibility(); if( event == null) return; if (!subscriber.isUnsubscribed()) { subscriber.onNext(event); } } }; mTopView.getViewTreeObserver().addOnGlobalLayoutListener(listener); subscriber.add(new MainThreadSubscription() { @Override protected void onUnsubscribe() { mTopView.getViewTreeObserver().removeOnGlobalLayoutListener(listener); } }); } } 

我有一个问题。

我将windowDrawsSystemBarBackgrounds设置为“true”,我的应用程序应该显示在状态栏下。

这是我的活动主题。

 <item name="android:windowTranslucentStatus" tools:targetApi="KITKAT">false</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:windowTranslucentNavigation">true</item> <item name="android:statusBarColor">@android:color/transparent</item> 

我从build树博客那里得到了帮助 你可以阅读代码,但像我这样的文字。 我多添加一些代码。

 public final class ZeroInsetsFrameLayout extends FrameLayout { private int[] mInsets = new int[4]; public ZeroInsetsFrameLayout(Context context) { super(context); } public ZeroInsetsFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); } public ZeroInsetsFrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public final int[] getInsets() { return mInsets; } @Override public WindowInsets computeSystemWindowInsets(WindowInsets in, Rect outLocalInsets) { outLocalInsets.left = 0; outLocalInsets.top = 0; outLocalInsets.right = 0; return super.computeSystemWindowInsets(in, outLocalInsets); } @Override protected final boolean fitSystemWindows(@NonNull Rect insets) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // Intentionally do not modify the bottom inset. For some reason, // if the bottom inset is modified, window resizing stops working. // TODO: Figure out why. mInsets[0] = insets.left; mInsets[1] = insets.top; mInsets[2] = insets.right; insets.left = 0; insets.top = 0; insets.right = 0; } return super.fitSystemWindows(insets); } } 

这是我的片段布局。

 <com.dhna.widget.ZeroInsetsFrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:background="@color/white"> <!-- your xml code --> </ZeroInsetsFrameLayout> 

我希望它对你有帮助。 祝你好运!

最佳实践允许用户在显示键盘时滚动内容。 因此,要添加此function,您需要将您的根布局放入ScrollView并使用windowSoftInputMode="adjustResize"活动方法。

但是,如果要在Android 5内容上使用<item name="android:windowTranslucentStatus">true</item>标志,则该function将不可滚动,并且与键盘重叠。

要解决这个问题,请检查这个答案