Android Jetpack Compose 沉浸式/透明状态栏 ProvideWindowInsets SystemUiController 前言代码解析添加依赖FitsSystemWindows透明状态栏内容不挡住状态栏 完事
前言
从现在开始疯狂学习Jetpack Compose,经过一早上的研究带来第一篇文章,沉浸式/透明状态栏
代码懒得看研究过程的朋友可以直接参考最终结果:
import android.content.Intentimport android.os.Bundleimport androidx.activity.ComponentActivityimport androidx.activity.compose.setContentimport androidx.core.view.WindowCompatimport androidx.compose.material.*import androidx.compose.runtime.Composableimport androidx.compose.runtime.SideEffectimport androidx.compose.ui.Modifierimport androidx.compose.ui.graphics.Colorimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.tooling.preview.Previewimport androidx.compose.ui.unit.dpimport com.google.accompanist.insets.LocalWindowInsetsimport com.google.accompanist.insets.ProvideWindowInsetsimport com.google.accompanist.insets.rememberInsetsPaddingValuesimport com.google.accompanist.insets.statusBarsPaddingimport com.google.accompanist.systemuicontroller.rememberSystemUiControllerclass MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) setContent { BD_ToolTheme { ProvideWindowInsets { val systemUiController = rememberSystemUiController() SideEffect { systemUiController.setStatusBarColor(Color.Transparent, darkIcons = false) } Surface( color = MaterialTheme.colors.background ) { Column { com.google.accompanist.insets.ui.TopAppBar( title = { Text(text = "首页") }, contentPadding = rememberInsetsPaddingValues( insets = LocalWindowInsets.current.statusBars) ) Text(text = "你好") } } } } } }}效果如图:
本篇文章的所有了解来源于这个链接https://google.github.io/accompanist/insets/
当然了,纯英文的,带着翻译工具冲就完事了。
添加依赖 repositories { mavenCentral()}dependencies { implementation "com.google.accompanist:accompanist-insets:$accompanist_version" implementation "com.google.accompanist:accompanist-insets-ui:$accompanist_version" implementation "com.google.accompanist:accompanist-systemuicontroller:$accompanist_version"}我写下这篇文章时,最新版本为 accompanist_version = "0.15.0"
FitsSystemWindows第一行代码
我们跳转到源码去看看描述
/** * Sets whether the decor view should fit root-level content views for WindowInsetsCompat. * If set to false, the framework will not fit the content view to the insets and will just pass through the WindowInsetsCompat to the content view. * Please note: using the View.setSystemUiVisibility(int) API in your app can conflict with this method. Please discontinue use of View.setSystemUiVisibility(int). * Params: * window – The current window. * decorFitsSystemWindows – Whether the decor view should fit root-level content views for insets. */public static void setDecorFitsSystemWindows(@NonNull Window window, final boolean decorFitsSystemWindows) { if (Build.VERSION.SDK_INT >= 30) { Impl30.setDecorFitsSystemWindows(window, decorFitsSystemWindows); } else if (Build.VERSION.SDK_INT >= 16) { Impl16.setDecorFitsSystemWindows(window, decorFitsSystemWindows); } }可以看出,当设置成false,就不再使用原定的间隔框架,我们的页面也就不再受状态栏的影响。
If set to false, the framework will not fit the content view to the insets and will just pass through the WindowInsetsCompat to the content view.
如果设置为false,框架将不会将内容视图与嵌入物相适应,而只是将WindowInsetsCompat传递给内容视图。
文字描述总是乏味的,上代码
首先,将decorFitsSystemWindows设置为true override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, true) setContent { BD_ToolTheme { Text(text = "首页rn首页1rn首页2rn首页3") } } }效果如下:
可以看到第一个首页,是紧贴着状态栏的。
接着,将decorFitsSystemWindows设置为false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) setContent { BD_ToolTheme { Text(text = "首页rn首页1rn首页2rn首页3") } } }效果如下:
可以看到第一个首页,其实已经藏在了状态栏的后面了。
透明状态栏上一步我们已将内容铺满全屏了,这一步就应该将状态改成透明,将我们的内容显示出来。
而这一步我们用到的就是SystemUiController。
拿到 SystemUiController 的实例,是使用rememberSystemUiController()这个方法,看到remember开头,就知道这是一个观察者的值,然后使用 SystemUiController 的实例去调用方法setStatusBarColor,这个方法有3个参数
我们设置颜色为透明Color.Transparent,是否为深色图标就根据是否在深色模式来判断MaterialTheme.colors.isLight
内容不挡住状态栏上一步已经将内容显示出来的,那么怎么将它往下移一点,不要和状态栏给重叠了呢?
这就要用到ProvideWindowInsets了。我们需要在显示内容的外围包一层ProvideWindowInsets,这样子,我们的显示内容才能读取到真正的状态栏高度之类的值。
举例:我们先不用ProvideWindowInsets
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) setContent { BD_ToolTheme { rememberSystemUiController().setStatusBarColor( Color.Transparent, darkIcons = MaterialTheme.colors.isLight) Column { // 先显示一个空白在上面,高度是状态栏高度 Spacer(modifier = Modifier .statusBarsHeight() .fillMaxWidth()) Text(text = "首页rn首页1rn首页2rn首页3") } } } }
可以看出Spacer并没有生效,说明这时候读取到的状态栏高度是0.
这个时候我们加入ProvideWindowInsets
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) setContent { BD_ToolTheme { // 加入ProvideWindowInsets ProvideWindowInsets { rememberSystemUiController().setStatusBarColor( Color.Transparent, darkIcons = MaterialTheme.colors.isLight) Column { Spacer(modifier = Modifier .statusBarsHeight() .fillMaxWidth()) Text(text = "首页rn首页1rn首页2rn首页3") } } } } }
明显可以看出Spacer生效了,能读取到系统状态栏的高度了
可以在跳转进statusBarsHeight()这个方法,可以看到其实是读取了这个值:LocalWindowInsets.current.statusBars,所以也可以改写成rememberInsetsPaddingValues(insets = LocalWindowInsets.current.statusBars)
完事到这里基本的逻辑已经讲完了,可能有同学发现有个东西我没有讲到SideEffect,为什么需要用这个来包着我们的状态栏颜色设置呢?这个有机会再开一篇文章来说。