【问题标题】:OnApplyWindowInsetsListener gives systemWindowInsetBottom that is always 0OnApplyWindowInsetsListener 给出始终为 0 的 systemWindowInsetBottom
【发布时间】:2019-04-27 10:06:27
【问题描述】:

systemWindowInsetBottom 和 stableInsetBottom 都始终为 0

我有一个具有背景纹理的 Activity,并且我正在使用 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 的实际设备上获得相同的结果。

如果导航栏存在,我需要做什么才能获得导航栏的实际大小?

【问题讨论】:

    标签: android windowinsets


    【解决方案1】:

    我在发布之前花了大约 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
    }
    

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

    确保使用 android:fitsSystemWindows="true"

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

    最后在你的 setOnApplyWindowInsetsListener 中做点什么

    【讨论】:

      【解决方案2】:

      为了获得完全透明的状态栏和导航栏,并应用了插图,我必须这样做:

      我的根视图是ContstraintLayout。不知何故,即使没有fitsSystemWindows,它也能正常工作,而且我得到了顶部和底部的插图,我已将其应用于我的 Toolbar 和 FrameLayout 边距。

      在主题中:

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

      setImmersiveActivity 中的函数:

      我创建了这个函数来应用实现沉浸式外观所需的插入和窗口标志,以获取可以具有的布局:

      • 根视图
        • 顶视图
        • 内容
        • 底视图
          /**
           * 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>
      

      【讨论】:

        猜你喜欢
        • 2020-01-07
        • 2023-03-10
        • 2014-01-09
        • 1970-01-01
        • 2011-08-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多