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); } }
С
fitSystemWindow
s был устаревшим, пожалуйста, обратитесь к ответу ниже, чтобы завершить обходной путь.
@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);