替换片段时,Android fitsSystemWindows无法正常工作

时间:2016-09-03 13:52:56

标签: 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="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中的Fragments。当我在fitsSystemWindows布局上将Fragment设置为true时,它没有响应。实际上它仅在创建Activity时有效,但是当我替换Fragment内的FrameLayout后,fitsSystemWindows参数将被忽略,布局位于状态栏下方导航栏。

我找到了一些solution自定义FrameLayout,它使用了弃用的方法,但由于某种原因它不适用于我(与普通FrameLayout相同的结果)我也不喜欢使用弃用方法的想法。

4 个答案:

答案 0 :(得分:8)

您的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;
}

您可以查看my this答案,其中将详细说明其工作原理,以及如何使用ViewCompat.setOnApplyWindowInsetsListener API。

答案 1 :(得分:3)

您还可以构建自定义decltype并使用WindowInsetsFrameLayout请求再次应用插入内容:

OnHierarchyChangedListener

查看详细答案:https://stackoverflow.com/a/47349880/3979479

答案 2 :(得分:1)

我认为问题围绕着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,这允许多个直接孩子拥有{{1} }。如果你有一个CoordinatorLayout,你的里程可能会有所不同。

Android中的这整个功能似乎是一个不圣洁的混乱。

答案 3 :(得分:0)

a)您可以将CoordinatorLayout用作片段内的根视图

b)您可以创建自定义线性布局,将其称为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()
    }
}