新しいAndroid KitKat(4.4)およびwindowSoftInputMode="adjustResize"
の半透明のactionbar/navbarに問題があります。
通常、InputModeをadjustResizeに変更すると、キーボードが表示されたときにアプリ自体のサイズが変更されますが、ここでは表示されません!透明効果の線を削除すると、サイズ変更が機能します。
そのため、キーボードが表示されている場合、リストビューがその下にあり、最後のいくつかのアイテムにアクセスできません。 (キーボードを手動で非表示にすることによってのみ)
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>
values-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>
これを修正するためのアイデアはありますか?
次のプロパティがありません:
Android:fitsSystemWindows="true"
fragment.xmlレイアウトのルートRelativeLayout
.
更新:
昨年、Chris Baneによる興味深い講演があり、この仕組みについて詳しく説明しています。
関連するバグレポートがあります こちら 。私は、限られたテストから、影響なしでトリックを行うように見える回避策を見つけました。以下のロジックを使用して、ルートViewGroup
(ほとんどの場合FrameLayout
を使用しているため、これをテストしました)のカスタム実装を追加します。次に、ルートレイアウトの代わりにこのカスタムレイアウトを使用し、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 (i.e.
* {@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
sは廃止されたため、次の回答を参照して回避策を完了してください。
@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をするために働いた:
@ Victor91と@kcoppockが言ったように、カスタムRelativeLayoutを作成します。
フラグメントの親レイアウトとしてCustomRelativeLayoutを使用します。
Android:windowTranslucentStatus = trueでテーマを宣言します
コンテナのアクティビティは、Android:windowSoftInputMode = "adjustResize"を使用してマニフェストで宣言し、宣言したテーマを使用する必要があります
フラグメントルートレイアウトで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>
インセットをカスタマイズし、21以上のAPIレベルをターゲットにしている場合、カスタムビューグループを作成しなくてもこれを達成できます。 fitsSystemWindows
を設定するだけで、デフォルトでパディングがコンテナビューに適用されますが、これは望ましくない場合があります。
バージョンチェックはこのメソッドに組み込まれ、21以上のデバイスのみがラムダ内のコードを実行します。 Kotlinの例:
ViewCompat.setOnApplyWindowInsetsListener(container) { view, insets ->
insets.replaceSystemWindowInsets(0, 0, 0, insets.systemWindowInsetBottom).apply {
ViewCompat.onApplyWindowInsets(view, this)
}
}
レイアウトでfitsSystemWindows
フラグが設定されていることを確認してください。設定されていない場合、ウィンドウインセットリスナーは呼び出されません。
<FrameLayout
Android:id="@+id/container"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:fitsSystemWindows="true"
/>
これらのソースは役に立ちます:
https://medium.com/google-developers/why-would-i-want-to-fitssystemwindows-4e26d9ce1eechttps://medium.com/@azizbekian/windowinsets-24e241d4afb9
私は同じ問題を抱えていました。私のアクティビティにはルートビューとしてScrollViewがあり、半透明のステータスバーがアクティブになっているため、キーボードが表示されたときに正しくサイズが変更されませんでした...
解決策:すべて(レイアウトとアクティビティロジック)を新しいフラグメント内に移動しました。次に、アクティビティを変更して、このフラグメントのみを含めるようにしました。これですべてが期待どおりに機能するようになりました!
これは、アクティビティのレイアウトです。
<?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でのジョセフジョンソンの回避策に基づいていますソフトキーボードが表示されているときにフルスクリーンモードでレイアウトを調整する方法
アクティビティのonCreate()
の後にsetContentView()
でこれを呼び出します。
AndroidBug5497Workaround.assistActivity(this);
元とは異なるリッテは、return (r.bottom - r.top);
のcomputeUsableHeight()
をreturn r.bottom;
に置き換えます
何らかの理由で、アクティビティ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;
}
}
同じ問題がありました。 coordinatorlayoutを使用して解決しました
activity.main.xml
<?xml version="1.0" encoding="utf-8"?>
<Android.support.design.widget.CoordinatorLayout
Android:layout_height="match_parent" Android:layout_width="match_parent"
xmlns:tools="http://schemas.Android.com/tools"
xmlns:app="http://schemas.Android.com/apk/res-auto"
xmlns:Android="http://schemas.Android.com/apk/res/Android">
<Android.support.design.widget.AppBarLayout
Android:layout_height="wrap_content"
Android:layout_width="match_parent"
Android:theme="@style/AppTheme.AppBarOverlay">
<Android.support.v7.widget.Toolbar
Android:layout_height="?attr/actionBarSize"
Android:layout_width="match_parent"
app:popupTheme="@style/AppTheme.PopupOverlay"
Android:background="?attr/colorPrimary"
Android:id="@+id/toolbar"/>
</Android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main2"/>
</Android.support.design.widget.CoordinatorLayout>
content_main2.xml
<?xml version="1.0" encoding="utf-8"?>
<Android.support.constraint.ConstraintLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
xmlns:app="http://schemas.Android.com/apk/res-auto">
<Android.support.v7.widget.RecyclerView
Android:layout_height="match_parent"
Android:layout_width="match_parent"
Android:layout_marginTop="30dp"
Android:layout_marginBottom="30dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
Android:id="@+id/post_msg_recyclerview">
</Android.support.v7.widget.RecyclerView>
<EditText
Android:layout_width="match_parent"
Android:layout_height="50dp"
app:layout_constraintBottom_toBottomOf="parent"
Android:background="@color/colorPrimary"
/>
</Android.support.constraint.ConstraintLayout>
MainActivity.Java
この行を追加しますlinearLayoutManager.setStackFromEnd(true);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(linearLayoutManager);
Adapter adapter1=new Adapter(arrayList);
recyclerView.setAdapter(adapter1);
AndroidBug5497Workaround.Javaはメモリリークに注意します。以下のコードが必要
getViewTreeObserver().removeOnGlobalLayoutListener(listener);
アクティビティのライフサイクルでonPause()のときにremoveOnGlobalLayoutListener()を自動的に呼び出すRxJavaを使用した私のサンプル
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);
}
});
}
}
ベストプラクティスでは、キーボードが表示されているときにユーザーがコンテンツをスクロールできます。したがって、この機能を追加するには、ルートレイアウトをScrollView
内に配置し、windowSoftInputMode="adjustResize"
アクティビティメソッドを使用する必要があります。
ただし、Androidの<item name="Android:windowTranslucentStatus">true</item>
フラグでこの機能を使用する場合、5つのコンテンツはスクロール可能ではなく、キーボードと重複します。
この問題を解決するには、これを確認してください answer
XML
<RelativeLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:fitsSystemWindows="true">
<!-- Your xml -->
</RelativeLayout>
アクティビティ
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView("Your Activity");
setAdjustScreen();
}
作成された機能
protected void setAdjustScreen(){
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
/*Android:windowSoftInputMode="adjustPan|adjustResize"*/
}
最後に、mainifestにいくつかの行を追加します
<activity
Android:name="Your Activity"
Android:windowSoftInputMode="adjustPan|adjustResize"
Android:screenOrientation="portrait"></activity>
<androidx.constraintlayout.widget.ConstraintLayout
Android:fitsSystemWindows="true">
<androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.Android.material.appbar.AppBarLayout>
<com.google.Android.material.appbar.CollapsingToolbarLayout/>
</com.google.Android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView>
<Editext/>
<androidx.core.widget.NestedScrollView/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
半透明のステータスバーでは機能しません。この設定により、ウィンドウは強制的にフルスクリーンモードになり、adjustResizeでは機能しません。
AdjustPanを使用するか、fitsSystemWindowsプロパティを使用できます。ただし、この機能について読むことをお勧めしますが、重大な副作用があります。
https://medium.com/google-developers/why-would-i-want-to-fitssystemwindows-4e26d9ce1eec
私は問題が好きでした。
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>
そして、私は jianshuのブログ から助けを得ました。私のようなコードでもテキストでも読むことができます。さらにいくつかのコードを追加します。
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>
あなたの役に立つことを望みます。幸運を!