Android Jetpack Compose 沉浸式/透明状态栏 ProvideWindowInsets SystemUiController

Android Jetpack Compose 沉浸式/透明状态栏 ProvideWindowInsets SystemUiController

前言

从现在开始疯狂学习Jetpack Compose,经过一早上的研究带来第一篇文章,沉浸式/透明状态栏

代码

懒得看研究过程的朋友可以直接参考最终结果:

import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.core.view.WindowCompat
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.accompanist.insets.LocalWindowInsets
import com.google.accompanist.insets.ProvideWindowInsets
import com.google.accompanist.insets.rememberInsetsPaddingValues
import com.google.accompanist.insets.statusBarsPadding
import com.google.accompanist.systemuicontroller.rememberSystemUiController

class 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

第一行代码
在这里插入图片描述

WindowCompat.setDecorFitsSystemWindows(window, false)

我们跳转到源码去看看描述

/**
 * 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 = "首页\r\n首页1\r\n首页2\r\n首页3")
            }
        }
    }

效果如下:
在这里插入图片描述

可以看到第一个首页,是紧贴着状态栏的。

  • 接着,将decorFitsSystemWindows设置为false
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        WindowCompat.setDecorFitsSystemWindows(window, false)

        setContent {
            BD_ToolTheme {
                Text(text = "首页\r\n首页1\r\n首页2\r\n首页3")
            }
        }
    }

效果如下:
在这里插入图片描述

可以看到第一个首页,其实已经藏在了状态栏的后面了。

透明状态栏

上一步我们已将内容铺满全屏了,这一步就应该将状态改成透明,将我们的内容显示出来。
而这一步我们用到的就是SystemUiController。

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        WindowCompat.setDecorFitsSystemWindows(window, false)

        setContent {
            BD_ToolTheme {
            	rememberSystemUiController().setStatusBarColor(
                    Color.Transparent, darkIcons = MaterialTheme.colors.isLight)
                Text(text = "首页\r\n首页1\r\n首页2\r\n首页3")
            }
        }
    }

在这里插入图片描述
拿到 SystemUiController 的实例,是使用rememberSystemUiController()这个方法,看到remember开头,就知道这是一个观察者的值,然后使用 SystemUiController 的实例去调用方法setStatusBarColor,这个方法有3个参数

fun setStatusBarColor(
	// 状态栏的颜色
    color: Color,
    // 是否为深色图标
    darkIcons: Boolean = color.luminance() > 0.5f,
    // 如果要求使用深色图标但没有,将调用一个lambda来转换颜色。默认情况下是应用一个黑色框框。
    transformColorForLightContent: (Color) -> Color = BlackScrimmed
)

我们设置颜色为透明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 = "首页\r\n首页1\r\n首页2\r\n首页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 = "首页\r\n首页1\r\n首页2\r\n首页3")
                    }
                }
            }
        }
    }

在这里插入图片描述
明显可以看出Spacer生效了,能读取到系统状态栏的高度了

可以在跳转进statusBarsHeight()这个方法,可以看到其实是读取了这个值:LocalWindowInsets.current.statusBars,所以也可以改写成rememberInsetsPaddingValues(insets = LocalWindowInsets.current.statusBars)

完事

到这里基本的逻辑已经讲完了,可能有同学发现有个东西我没有讲到SideEffect,为什么需要用这个来包着我们的状态栏颜色设置呢?这个有机会再开一篇文章来说。

  • 16
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值