Android подходит SystemWindows не работает при замене фрагментов
Я имею SingleFramgnetActivity
чья цель только держать и заменять фрагменты внутри него.
макет выглядит так:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
tools:context=".SingleFragmentActivity"
>
<include layout="@layout/toolbar"/>
<FrameLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
Я заменяю Fragments
внутри FrameLayout. Когда я установил fitsSystemWindows
правда на Fragment
макет, он не отвечает. На самом деле это работает только тогда, когда Activity
создается, но как только я заменяю Fragment
внутри FrameLayout
, fitsSystemWindows
параметр игнорируется, а макет находится ниже строки состояния и панели навигации.
Я нашел какое-то решение с пользовательским FrameLayout, использующим устаревшие методы, но по какой-то причине оно не работает для меня (тот же результат, что и с обычным FrameLayout), и мне также не нравится идея использовать устаревшие методы.
3 ответа
Ваш FrameLayout
не знает о размерах вставки окна, потому что это родитель - LinearLayout
не отправил его никому. В качестве обходного пути, вы можете подкласс LinearLayout
и передать вкладыши детям самостоятельно:
@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
int childCount = getChildCount();
for (int index = 0; index < childCount; index++)
getChildAt(index).dispatchApplyWindowInsets(insets); // let children know about WindowInsets
return insets;
}
Вы можете посмотреть на мой ответ, который подробно объяснит, как это работает, а также как использовать ViewCompat.setOnApplyWindowInsetsListener
API.
Вы также можете построить кастом WindowInsetsFrameLayout
и использовать OnHierarchyChangedListener
запросить повторное нанесение вкладышей:
public WindowInsetsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// Look for replaced fragments and apply the insets again.
setOnHierarchyChangeListener(new OnHierarchyChangeListener() {
@Override
public void onChildViewAdded(View parent, View child) {
requestApplyInsets();
}
@Override
public void onChildViewRemoved(View parent, View child) {
}
});
}
Проверьте этот подробный ответ: /questions/33578356/effekt-fitssystemwindows-dlya-fragmentov-dobavlennyih-cherez-fragmenttransaction/33578380#33578380
А ) вы можете использовать CoordinatorLayout в качестве корневого представления внутри фрагмента
или
б) вы можете создать собственный линейный макет, который будет вызывать requestApplyInsets и использовать его в качестве корневого представления внутри фрагмента
class WindowInsetsLinearLayout : LinearLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun onAttachedToWindow() {
super.onAttachedToWindow()
ViewCompat.requestApplyInsets(this)
}
}
а затем внутри фрагмента ловить вставки
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ViewCompat.setOnApplyWindowInsetsListener(root_layout) { _, insets ->
//appbar.setPadding(insets.systemWindowInsetLeft, insets.systemWindowInsetTop, 0, 0)
insets.consumeSystemWindowInsets()
}
}
Я думаю, что проблема вращается вокруг onApplyWindowInsets
вызов до того, как иерархия представления фрагмента будет присоединена. Эффективным решением является получение следующего переопределения для представления где-то в иерархии представлений фрагмента.
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// force window insets to get re-applied if we're being attached by a fragment.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
requestApplyInsets();
} else {
//noinspection deprecation
requestFitSystemWindows();
}
}
Полное решение (если вам не нужно использовать CoordinatorLayout
) следует Удостовериться fitSystemWindows=true
не появляется где-либо в представлениях выше в иерархии. Может быть, не где-нибудь еще. Я подозреваю (но не уверен), что consumeSystemWindowInsets
Съедает вставки для представлений, которые находятся далее в порядке расположения дерева представлений.
package com.twoplay.xcontrols;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.WindowInsets;
import android.widget.FrameLayout;
public class FitSystemWindowsLayout extends FrameLayout {
private boolean mFit = true;
public FitSystemWindowsLayout(final Context context) {
super(context);
init();
}
public FitSystemWindowsLayout(final Context context, final AttributeSet attrs) {
super(context, attrs);
init();
}
public FitSystemWindowsLayout(final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setFitsSystemWindows(true);
}
public boolean isFit() {
return mFit;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
requestApplyInsets();
} else {
//noinspection deprecation
requestFitSystemWindows();
}
}
public void setFit(final boolean fit) {
if (mFit == fit) {
return;
}
mFit = fit;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
requestApplyInsets();
} else {
//noinspection deprecation
requestFitSystemWindows();
}
}
@SuppressWarnings("deprecation")
@Override
protected boolean fitSystemWindows(final Rect insets) {
if (mFit) {
setPadding(
insets.left,
insets.top,
insets.right,
insets.bottom
);
return true;
} else {
setPadding(0, 0, 0, 0);
return false;
}
}
@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
@Override
public WindowInsets onApplyWindowInsets(final WindowInsets insets) {
if (mFit) {
setPadding(
insets.getSystemWindowInsetLeft(),
insets.getSystemWindowInsetTop(),
insets.getSystemWindowInsetRight(),
insets.getSystemWindowInsetBottom()
);
return insets.consumeSystemWindowInsets();
} else {
setPadding(0, 0, 0, 0);
return insets;
}
}
}
Подозрение, а не факт: только один вид во всей иерархии получает возможность съесть вставки окна, ЕСЛИ У вас нет CoordinatorLayout
в иерархии, которая позволяет иметь более одного прямого потомка FitSystemWindow=true
, Если у вас есть CoordinatorLayout, ваш пробег может отличаться.
Вся эта функция в Android кажется безобразной путаницей.