OnApplyWindowInsetsListener提供的systemWindowInsetBottom始终为0

时间:2018-11-25 16:42:14

标签: android

systemWindowInsetBottom和stableInsetBottom都始终为0

我有一个具有背景纹理的“活动”,并且我正在使用FLAG_LAYOUT_NO_LIMITS将背景放到状态和导航栏的后面。但是我不希望视图的其他内容位于这些系统UI组件的后面。

起初我想过使用resources.getIdentifier("navigation_bar_height", "dimen", "android")来获取导航栏的高度,但这只是导航栏的默认高度,因此它在用户隐藏了导航栏的设备上将无法使用。 (三星设备)

然后我发现了有关WindowInsets和android:fitsSystemWindows =“ true”

它适用于状态栏,但不适用于导航栏。

在我的活动中

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    //These 2 flags don't seem to change anything
    window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN)
    window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR)
    //This flag is required so the background can go behind the navbar
    window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)

    rootView.setOnApplyWindowInsetsListener { _, insets ->
        val topTextViewParams = rootView.tvTop.layoutParams as ViewGroup.MarginLayoutParams
        topTextViewParams.topMargin = insets.systemWindowInsetTop

        val bottomTextViewParams = rootView.tvBottom.layoutParams as ViewGroup.MarginLayoutParams
        bottomTextViewParams.bottomMargin = insets.systemWindowInsetBottom //Inset is always 0

        insets.consumeSystemWindowInsets()
    }
}

我的布局

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        android:id="@+id/rootView"
        android:background="@color/colorPrimary"
        tools:context=".MainActivity">

    <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        <TextView
                android:id="@+id/tvBottom"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                android:text="Text aligned to bottom of layout with no margin"/>
        <TextView
                android:id="@+id/tvTop"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="8dp"
                android:layout_marginEnd="8dp"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                android:text="Text aligned to top of layout with no margin"/>
    </android.support.constraint.ConstraintLayout>
</android.support.design.widget.CoordinatorLayout>

运行API 28的Nexus 5x模拟器的屏幕截图示例。在运行API 26的实际设备上获得相同的结果。

Example Images

如果存在导航栏,我该怎么办?

2 个答案:

答案 0 :(得分:0)

在发布之前,我花了3个小时进行研究,然后在发布之后5分钟。

那5分钟非常有成果,因为我发现了this post,这促使我尝试添加:

<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>

运行到我的AppTheme。

因此,如果您要尝试执行此操作,请确保:

override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
    val childCount = childCount
    for (index in 0 until childCount)
        getChildAt(index).dispatchApplyWindowInsets(insets)
    // let children know about WindowInsets

    return insets
}

OR
像我在帖子中那样使用“实质性布局,例如DrawerLayout或CoordinatorLayout”。

确保使用android:fitsSystemWindows =“ true”

确保将这些属性添加到主题中。

最后在setOnApplyWindowInsetsListener中做一些事情

答案 1 :(得分:0)

这是我要获取完全透明的状态栏和导航栏并应用了插图的操作

我的根视图是ContstraintLayout。甚至在没有fitsSystemWindows的情况下,它仍然可以正常工作,并且我得到了顶部和底部的插图,这些插图已应用于ToolbarFrameLayout的边距。

主题:

<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowTranslucentNavigation">false</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
setImmersive中的

Activity函数:

我已经创建了此功能,以应用可实现具有以下效果的版面的沉浸式外观所需的插图和窗口标记:

  • 根视图
    • 顶视图
    • 内容
    • 底视图
    /**
     * Apply immersive mode to this activity
     * @param rootView the root layout which will receive the window insets
     * @param topView the top view to apply insets to (optional)
     * @param bottomView the bottom view to apply insets to (optional)
     */
    fun setImmersive(rootView: ViewGroup, topView: View? = null, bottomView: View? = null) {

        window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)

        rootView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        bottomView?.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

        rootView.setOnApplyWindowInsetsListener { _, insets ->
            Log.d(javaClass.simpleName + " windowInsets", insets.toString())

            // Apply the top insets
            if (topView != null)  {
                insets.systemWindowInsetTop.apply {
                    if (this > 0) {
                        Log.d(javaClass.simpleName, "applying windowInsetTop $this")
                        val layoutParams = topView.layoutParams as ViewGroup.MarginLayoutParams
                        layoutParams.topMargin = this
                        topView.layoutParams = layoutParams
                    }
                }
            }

            // Apply the bottom insets
            if (bottomView != null)  {
                insets.systemWindowInsetBottom.apply {
                    if (this > 0) {
                        Log.d(javaClass.simpleName, "applying windowInsetBottom $this")
                        val layoutParams = bottomView.layoutParams as ViewGroup.MarginLayoutParams
                        layoutParams.bottomMargin = this
                        bottomView.layoutParams = layoutParams
                    }
                }
            }

            insets.consumeSystemWindowInsets()
        }
    }

在您的onCreate中调用此函数,例如:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.brand_activity)
        setImmersive(root_view, topView = toolbar, bottomView = root_content)
}

在根视图上的OnApplyWindowInsetsListener被调用了两次,一次是正确插入WindowInsets{systemWindowInsets=Rect(0, 98 - 0, 168),第二次是0插入WindowInsets{systemWindowInsets=Rect(0, 0 - 0, 0)。因此,我仅在非零插图上设置边距。

在布局中:

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/root_view">

    <FrameLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:id="@+id/root_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/toolbar_layout"
        app:layout_constraintTop_toTopOf="parent">

        <androidx.appcompat.widget.Toolbar
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:id="@+id/toolbar">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/toolbar_title"
                android:textStyle="bold"
                android:layout_gravity="center" />

        </androidx.appcompat.widget.Toolbar>
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>