windowSoftInputMode= "adjustResize" не работает с полупрозрачным действием / navbar
у меня есть проблемы с полупрозрачной actionbar / navbar в новом Android KitKat (4.4) и windowSoftInputMode="adjustResize".
нормальное изменение InputMode для adjustResize, приложение должно изменить размер себя, когда клавиатура отображается... но здесь этого не будет! Если я удаляю строки для прозрачного эффекта, изменение размера работает.
поэтому, если клавиатура видна, мой 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 / стили.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>
фрагмент.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>
кто-нибудь идеи, чтобы это исправить?
13 ответов:
отсутствует следующее свойство:
android:fitsSystemWindows="true"в корень
RelativeLayoutна фрагмент .XML-макета.обновление:
в прошлом году был интересный разговор Криса Бэйна, который подробно объясняет, как это работает:
есть соответствующий отчет об ошибке здесь. Я нашел обходной путь, который из ограниченного тестирования, похоже, делает трюк без каких-либо последствий. Добавьте пользовательскую реализацию вашего root
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); } }С
fitSystemWindows был устаревшим, пожалуйста, обратитесь к ответу ниже, чтобы завершить обходной путь.
@kcoppock ответ действительно полезен, но fitSystemWindows был устаревшим в API level 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 в фрагменте:
сделать пользовательский RelativeLayout как @Victor91 и @kcoppock сказал.
используйте 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>
У меня была такая же проблема, У моей активности был 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 Как настроить макет в полноэкранном режиме, когда softkeyboard виден
звонок
onCreate()послеsetContentView()в вашей деятельности.
AndroidBug5497Workaround.assistActivity(this);немного отличается от оригинала заменить
return (r.bottom - r.top);Сreturn r.bottom;наcomputeUsableHeight()по какой-то причине, я должен установить мою деятельность до
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; } }
если вы хотите настроить вставки, и вы ориентируетесь на уровень API >=21, Вы можете выполнить это без необходимости создавать пользовательскую группу представлений. Просто установив
fitsSystemWindowsзаполнение будет применено к вашему представлению контейнера по умолчанию, которое вы можете не захотеть.проверки версий встроены в этот метод, и только устройства >= 21 будут выполнять код внутри лямбда. Котлин пример:
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-4e26d9ce1eec https://medium.com/@azizbekian/windowinsets-24e241d4afb9
AndroidBug5497Workaround.java позаботится об утечке памяти. нужно ниже код
getViewTreeObserver().removeOnGlobalLayoutListener(listener);мой пример с помощью RxJava, который автоматически вызывает removeOnGlobalLayoutListener() когда onPause () в жизненном цикле действия
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>и я получил помощь от 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>Я хочу, чтобы это было полезно для вас. удачи вам!
лучшая практика позволяет пользователю прокручивать содержимое, когда отображается клавиатура. Поэтому, чтобы добавить эту функциональность, вам нужно поместить свой корневой макет внутри
ScrollViewи использоватьwindowSoftInputMode="adjustResize"способ деятельности.но если вы хотите использовать эту функциональность с
<item name="android:windowTranslucentStatus">true</item>флаг на Android 5 содержание не будет прокручиваться и будет перекрываться с клавиатурой.чтобы решить эту проблему, проверьте этот ответ
Он не должен работать с полупрозрачной строкой состояния; Эта настройка переводит окно в полноэкранный режим, который не работает с adjustResize.
вы можете использовать adjustPan или использовать свойства fitsSystemWindows. Я бы предложил прочитать об этой функции, хотя она имеет значительные побочные эффекты:
https://medium.com/google-developers/why-would-i-want-to-fitssystemwindows-4e26d9ce1eec
- после того, как я исследовал на всем форуме. эти способы не могут помочь найти указать. Повезло, когда я попытался сделать это таким образом. Это помогает мне решить проблему
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(); }Создан Func
protected void setAdjustScreen(){ getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); /*android:windowSoftInputMode="adjustPan|adjustResize"*/ }наконец, добавив несколько строк в вашей манифеста
<activity android:name="Your Activity" android:windowSoftInputMode="adjustPan|adjustResize" android:screenOrientation="portrait"></activity>
у меня была та же проблема. Я решил с помощью coordinatorlayout
активности.главный.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);