更换碎片时Android fitsSystemWindows不工作
我有更换碎片时Android fitsSystemWindows不工作,android,android-layout,android-fragments,Android,Android Layout,Android Fragments,我有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="mat
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>
我正在替换FrameLayout中的片段。当我在Fragment
布局上将fitsystemwindows
设置为true时,它没有响应。实际上,它只有在创建了活动
时才起作用,但一旦我替换了框架布局
中的片段
,就会忽略fitsSystemWindows
参数,布局位于状态栏和导航栏下方
我发现一些自定义FrameLayout使用了不推荐的方法,但由于某些原因,它对我不起作用(与普通FrameLayout的结果相同),我也不喜欢使用不推荐的方法 您的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;
}
@TargetApi(Build.VERSION\u code.KITKAT\u WATCH)
@凌驾
公共窗口插页在应用窗口插页上(窗口插页插页){
int childCount=getChildCount();
for(int index=0;index
您可以查看答案,其中将详细解释如何工作以及如何使用API。您还可以构建自定义的WindowInsertsFrameLayout
,并使用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) {
}
});
}
查看此详细答案:我认为问题在于在附加片段视图层次结构之前调用ApplyWindowInsets
。一个有效的解决方案是在片段的视图层次结构中的某个视图上获得以下覆盖
@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
。如果你有一个协调的布局,你的里程可能会有所不同
Android中的整个功能似乎是一团混乱。a)您可以使用CoordinatorLayout作为片段内部的根视图
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)
}
}
或
b) 您可以创建称为RequestApplySets的自定义线性布局,并将其用作片段内的根视图
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()
}
}
您知道fitsSystemWindow的功能吗?我希望我能正确地理解它,基本上,当它设置为true时,视图及其所有子视图不应在系统窗口下显示为状态栏或导航栏。当设置为false时,它应显示在系统窗口下。所以基本上我可以设置,我想要呈现状态栏和导航栏(fitsSystemWindow=true)之间的整个片段布局,以及状态栏和导航栏下的背景图像(fitsSystemWindow=false)。我可以完全按照我想要的做,直到我不更换碎片。我也有同样的问题。最后,我制作了一个框架布局,保存窗口插入并将其重新分派给新的子对象。我把代码放在一个文件夹里。让我知道这是否对你有效!当支持低于20的API时,始终使用
ViewCompat.setOnApplyWindowInsetsListener
,因为@azizbekian您的解释很有用,但它仍然不能帮助我解决更改片段的问题。fitSystemWindows在第一次打开片段时在片段布局中工作,但如果片段替换了另一个片段,则会被忽略。我说的是非根视图上的fitSystemWindows-第二级。在第一个层面上,它是有效的。“活动”和“片段”中层次结构中的所有视图都是协调器布局,或者根据您的答案实现该方法。@lobzik,检查您的视图层次结构,找出不向其子视图分派插入的父视图。您可以使用ViewCompat.SetonapplyWindowInsettsListener
并检查哪个ViewGroup
准确地返回0作为插入大小。@azizbekian如果在替换片段之前工作正常,它怎么可能是一个视图而不发送插入?正如我从调试中看到的,当片段替换另一个片段时,片段中的根布局不会收到OnApplyWindowInset。但它在第一次打开时确实会接到这个电话。我不知道如何解决这个问题这似乎不适用于新的导航组件
。使用BottomNavigation和Navigation Controller,如果我重新选择BottomNavigation中的任何项目,fitsSystemWindows
将被忽略。有办法吗?参考是一个演示应用程序,我应该添加CoordinatorLayout
缓存它接收到的最后一个WindowInsets
,如果接收到后续的WindowInsets
,例如通过View#requestApplyInsets()
,则“新”插入将是Object#equal()
,因此它不会将其分派给其子行为
实例,因此层次结构中它下面的任何内容都不会被通知。在这种情况下,您需要手动分派。至于我,我还必须覆盖“onApplyWindowInsets”