systemWindowInsetBottom и stableInsetBottom оба всегда 0
У меня есть Activity, у которой есть фоновая текстура, и я использую FLAG_LAYOUT_NO_LIMITS, чтобы этот фон проходил за панелью состояния и навигации. Но я не хочу, чтобы другое содержимое представления шло за этими компонентами системного интерфейса.
Сначала я подумал об использовании resources.getIdentifier("navigation_bar_height", "dimen", "android")
для получения высоты панели навигации, но это просто высота панели навигации по умолчанию, поэтому она не будет работать на устройствах. где пользователь скрыл панель навигации. (Устройства Samsung)
Тогда я узнал о 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>
Примеры скриншотов из эмулятора Nexus 5x с API 28. Получение тех же результатов на моем реальном устройстве с API 26.
Что мне нужно сделать, чтобы получить реальный размер панели навигации, если она есть?
Вот что мне нужно было сделать, чтобы получить как полностью прозрачную строку состояния, так и панель навигации с примененными вставками:
Мой корневой вид - это ContstraintLayout
. Каким-то образом это работает даже без fitsSystemWindows
и я получаю вставки для верха и низа, которые я применил к FrameLayout
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>
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>
Я провожу около 3 часов, исследуя это, прежде чем отправлять сообщения, а затем 5 минут после публикации.
Эти 5 минут были очень плодотворными, потому что я нашел этот пост, который заставил меня попробовать добавить:
<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