使用FragmentTransaction添加的片段的适应系统窗口效果

时间:2015-07-02 17:03:45

标签: android android-layout android-fragments

我有一个带有导航抽屉和全出血碎片的活动(顶部的图像必须出现在Lollipop上的半透明系统栏后面)。虽然我有一个临时解决方案,其中片段通过简单地在Activity的XML中使用<fragment>标记来膨胀,但它看起来很好。

然后我不得不用<fragment>替换<FrameLayout>并执行片段事务,现在片段不再出现在系统栏后面,尽管fitsSystemWindows设置为true跨越所有必需的层次结构。

我认为<fragment>在Activity的布局中与其自身的膨胀之间可能存在一些差异。我用Google搜索并为KitKat找到了一些解决方案,但这些解决方案都不适用于我(Lollipop)。

activity.xml

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                        xmlns:app="http://schemas.android.com/apk/res-auto"
                                        android:id="@+id/drawer_layout"
                                        android:layout_height="match_parent"
                                        android:layout_width="match_parent"
                                        android:fitsSystemWindows="true">

    <FrameLayout
            android:id="@+id/fragment_host"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true">

    </FrameLayout>

    <android.support.design.widget.NavigationView
            android:id="@+id/nav_view"
            android:layout_height="match_parent"
            android:layout_width="wrap_content"
            android:layout_gravity="start"
            android:fitsSystemWindows="true"/>

</android.support.v4.widget.DrawerLayout>

fragment.xml之

<android.support.design.widget.CoordinatorLayout
        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">

    <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="224dp"
            android:fitsSystemWindows="true"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
...

activity.xml 就是这样的:

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                        xmlns:app="http://schemas.android.com/apk/res-auto"
                                        android:id="@+id/drawer_layout"
                                        android:layout_height="match_parent"
                                        android:layout_width="match_parent"
                                        android:fitsSystemWindows="true">

    <fragment xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:id="@+id/fragment"
              android:name="com.actinarium.random.ui.home.HomeCardsFragment"
              tools:layout="@layout/fragment_home"
              android:layout_width="match_parent"
              android:layout_height="match_parent"/>

    <android.support.design.widget.NavigationView
            android:id="@+id/nav_view"
            android:layout_height="match_parent"
            android:layout_width="wrap_content"
            android:layout_gravity="start"
            android:fitsSystemWindows="true"/>

</android.support.v4.widget.DrawerLayout>

5 个答案:

答案 0 :(得分:15)

我的问题与您的问题类似:我有底栏导航,它正在替换内容片段。现在,有些片段想要在状态栏上绘制(使用CoordinatorLayoutAppBarLayout),而其他片段则不是(使用ConstraintLayoutToolbar)。

ConstraintLayout
  FrameLayout
    [the ViewGroup of your choice]
  BottomNavigationView

ianhanniballake添加另一个CoordinatorLayout图层的建议不是我想要的,所以我创建了一个自定义FrameLayout来处理插图(就像他建议的那样),过了一段时间我遇到了这个解决方案,实际上代码不多:

activity_main.xml中

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.app.WindowInsetsFrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</android.support.constraint.ConstraintLayout>

WindowInsetsFrameLayout.java

/**
 * FrameLayout which takes care of applying the window insets to child views.
 */
public class WindowInsetsFrameLayout extends FrameLayout {

    public WindowInsetsFrameLayout(Context context) {
        this(context, null);
    }

    public WindowInsetsFrameLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    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) {

            }
        });
    }

}

答案 1 :(得分:3)

好的,在几个人指出fitsSystemWindows的工作方式不同之后,不应该在层次结构的每个视图中使用它,我继续尝试并从不同的视图中删除属性。

fitsSystemWindows = \

中的每个节点删除activity.xml后,我获得了预期的状态

答案 2 :(得分:1)

我去年创建了这个来解决这个问题:https://gist.github.com/cbeyls/ab6903e103475bd4d51b

编辑:确保您首先了解适合SystemSindows的功能。当您在View上设置它时,它基本上意味着:&#34;将此视图及其所有子项放在状态栏下方和导航栏上方#34;。在顶部容器上设置此属性是没有意义的。

答案 3 :(得分:1)

用Kotlin编写的另一种方法,

问题:

您使用的FrameLayout不会传播fitsSystemWindows="true"到他的孩子:

<FrameLayout
    android:id="@+id/fragment_host"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true" />

解决方案:

扩展FrameLayout类并覆盖函数onApplyWindowInsets(),以将窗口插图传播到附加的片段:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
class BetterFrameLayout : FrameLayout {

    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)

    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)

    override fun onApplyWindowInsets(windowInsets: WindowInsets): WindowInsets {
        childCount.let {
            // propagates window insets to children's
            for (index in 0 until it) {
                getChildAt(index).dispatchApplyWindowInsets(windowInsets)
            }
        }
        return windowInsets
    }
}

将此布局用作片段容器,而不是标准FrameLayout

<com.foo.bar.BetterFrameLayout
    android:id="@+id/fragment_host"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true" />

其他:

如果您想进一步了解此结帐Chris Banes博客文章Becoming a master window fitter

答案 4 :(得分:1)

分配窗口插图的另一个可怕的问题是,在深度优先搜索中,第一个使用窗口插图的视图会阻止层次结构中的所有其他视图看到窗口插图。

以下代码片段允许多个孩子处理窗口插图。如果您尝试将窗口插图应用于NavigationView(或CoordinatorLayout)外部的装饰,则非常有用。覆盖您选择的ViewGroup。

@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
    if (!insets.isConsumed()) {

        // each child gets a fresh set of window insets
        // to consume.
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            WindowInsets freshInsets = new WindowInsets(insets);
            getChildAt(i).dispatchApplyWindowInsets(freshInsets);
        }
    }
    return insets; // and we don't.
}

也有用:

@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
      return insets.consume(); // consume without adding padding!
}

允许布局该视图子级的普通普通视图而没有窗口插入。