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

Act*_*ine 52 android android-layout android-fragments

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

然后,我不得不更换<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>
Run Code Online (Sandbox Code Playgroud)

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">
...
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

ian*_*ake 80

当您使用<fragment>,布局在片段的恢复onCreateView直接连接代替<fragment>标签(你永远不会真正看到一个<fragment>标签,如果你看一下你的视图层次.

因此,在这种<fragment>情况下,你有

DrawerLayout
  CoordinatorLayout
    AppBarLayout
    ...
  NavigationView
Run Code Online (Sandbox Code Playgroud)

cheesesquare的工作方式类似.这样做是因为,在解释这个博客帖子,DrawerLayoutCoordinatorLayout都对如何不同的规则fitsSystemWindows适用于他们-他们都用它来插页他们的孩子的意见,但呼吁dispatchApplyWindowInsets()上的每个孩子,让他们获得了fitsSystemWindows="true"财产.

这与布局的默认行为有所不同,例如FrameLayout当你使用时fitsSystemWindows="true"消耗所有插图,盲目地应用填充而不通知任何子视图(这是博客文章的"深度优先"部分).

因此,当您<fragment>使用FrameLayout和FragmentTransactions 替换标记时,您的视图层次结构将变为:

DrawerLayout
  FrameLayout
    CoordinatorLayout
      AppBarLayout
      ...
  NavigationView
Run Code Online (Sandbox Code Playgroud)

当Fragment的视图插入到FrameLayout.该视图对传递fitsSystemWindows给子视图一无所知,因此您CoordinatorLayout永远不会看到该标志或执行其自定义行为.

解决问题实际上非常简单:用另一个替换你FrameLayoutCoordinatorLayout.这可以确保fitsSystemWindows="true"将传递到CoordinatorLayoutFragment中新填充的内容.

替代且同样有效的解决方案是创建自定义子类FrameLayout并覆盖onApplyWindowInsets()以分派给每个子节点(在您的情况下只是一个)或使用ViewCompat.setOnApplyWindowInsetsListener()方法拦截代码中的调用并从那里调度(不需要子类).更少的代码通常是最容易维护的,因此我不一定建议在CoordinatorLayout解决方案上使用这些路由,除非您对此有强烈的感觉.

  • 这是真正的解决方案.谢谢!考虑到这些信息,我写了一个FrameLayout的子类,它将WindowInsets调度给它的子节点,以达到与你建议的CoordinatorLayout类似的效果.对于任何感兴趣的人:https://gist.github.com/Pkmmte/d8e983fb9772d2c91688 (5认同)

Mar*_*ius 20

我的问题与你的问题类似:我有一个底栏导航,它正在替换内容片段.现在的一些片段要画在状态栏(有CoordinatorLayout,AppBarLayout),别人不(与ConstraintLayout,Toolbar).

ConstraintLayout
  FrameLayout
    [the ViewGroup of your choice]
  BottomNavigationView
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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

            }
        });
    }

}
Run Code Online (Sandbox Code Playgroud)