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 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 () в жизненном цикле Activity
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);