前言
android 启动时会使用一个 白色 或 黑色 的启动背景,在 app 界面成功加载后,启动背景被 app 界面覆盖。
若 app 启动时,没有什么耗时的异步操作,可以秒载入界面,那么就没必要继续往下看了,因为没必要再弄一个启动屏耽误事,但如果启动后需要初始化一些信息才能显示界面,期望在异步载入时显示一个自定义欢迎屏,请继续
方案一
做一个静态 activey 欢迎页,启动后立即显示这个,待实际界面准备好之后,替换这个 activey 为实际界面,比如 react-native-splash-screen 就是采用的这种方案。
但这带来一个问题,就是这个欢迎页本身的绘制也需要时间,虽然非常快,但启动时第一眼看到的仍然会是一闪而过的启动背景,即白屏。消除这个白屏可以在 android/app/src/main/res/values/styles.xml
进行样式设置(下面是 RN 的 theme)
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
.....
<!--启动背景改为透明-->
<item name="android:windowIsTranslucent">true</item>
</style>
</resources>
经过上面的修改,没有启动白屏了,但会发现,点击 app 图标后,会有一下停顿感,然后显示欢迎屏。其实很好理解,这个停顿感其实就是正在显示透明的启动背景,等同于把白屏体验改为停顿感了。
方案二
既然 app 启动瞬间显示的是启动背景,那么直接美化启动背景,当做欢迎屏不就可以了嘛。
同样是修改 android/app/src/main/res/values/styles.xml
,改为下面这样
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
...
<!--设置启动背景为一个资源文件-->
<item name="android:windowBackground">@drawable/splash</item>
</style>
</resources>
创建启动背景资源文件 @drawable/splash
,保存路径为 android/app/src/main/res/drawable/splash.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item><color android:color="#ffffff"/></item>
<item>
<bitmap android:src="@drawable/splash_img"
android:antialias="true"
android:filter="true"
android:gravity="center" />
</item>
</layer-list>
启动背景资源文件解释:
-
第一个
item
设置背景,因为 android 有众多分辨率,不可能启动背景图刚好覆盖满,所以图片的边缘最好是纯色,通过设置背景颜色自动衔接即可。 -
@drawable/splash_img
指定启动背景图,参考 android不同分辨率适配,drawable
文件可以创建一个,也可以创建多个,当屏幕分辨率对上号了,优先使用最匹配的drawable
,若匹配不上,则选用最接近的一个,并按照比例进行缩放;一般情况下,只需针对最大像素密度(当前为
drawable-xxhdpi
)制作一张图片即可,当用户分辨率小时,会使用该图片并自动按照像素密度缩小,可能导致图片锐化。可以测试一下,如果锐化导致图片过于失真,可考虑多制作几张,如drawable/splash_img.png
、drawable-hdpi/splash_img.png
等等; -
Bitmap配置,可根据文档自行调整图片位置;若碰到较为复杂的启动屏,单纯一张图不好搞时,可看看九宫格类型图片(NinePatchXml, .9图片) 是否可实现需求。
React-Native JS 端
以上是 android 启动背景配置,不是针对 RN 的,只要是 android 项目都适合;
最后说一下 RN 的 JS 部分,一般情况下,我们会在 app 启动时,初始化一些东西,期望在初始化完成前,持续显示启动屏,那么只需要在入口 app.js 完成初始化之后再渲染界面即可,代码示例:
export default class extends React.Component {
state = {
ready: false
}
componentDidMount() {
// 执行完异步任务后, 开始绘制
doAsyncWork().then(() => {
this.setState({
ready:true
});
})
}
render(){
if (!this.state.ready) {
return null;
}
return <View/>;
}
}
番外:启动配置
- 上面的配置为最简化配置,启动背景将显示在 【顶部状态】 与 【底部虚拟导航栏】 之间;
- 其中底部虚拟导航栏可能没有,比如之前的手机是物理导航键,新的全面屏手机,但这都不影响适配,启动背景肯定在可用区域内。
- 若想针对性优化,需考虑状态栏和导航栏:文字是深色/浅色、背景是纯色/半透明/完全透明、状态栏是否下沉为沉浸式、导航栏区域是否为沉浸式
以下是实测结果:
- 无 API 版本限制的在
android/app/src/main/res/values/styles.xml
中配置即可 - 有 API 版本限制,比如最低 API Level 19,在
android/app/src/main/res/values-v19/styles.xml
中配置 - 如 API Level 19 仍需要通用配置的话,需将配置从
/values/styles.xml
复制到values-v19/styles.xml
,换句话说,每个配置都是独立的,而不是继承。
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!--
通用: 全屏显示,状态栏不显示,启动背景显示到最顶端。
但对于有刘海屏的机器,状态栏会变成一个大黑边,无字
对于有虚拟导航栏的手机,虚拟导航栏以浮动的状态位于底部,
即启动背景最下方会被导航栏覆盖
-->
<item name="android:windowFullscreen">true</item>
<!--
API 28: 在 windowFullscreen 开启后,通过该设置可去除刘海屏手机顶部的大黑边
启动背景将显示到手机的最顶端。
-->
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<!--
API 21: 在 windowFullscreen 开启后,通过该属性设置为 false
启动背景最低端 为 导航栏上沿,即导航栏不会覆盖启动背景了
低版本无法使用,不过低版本 android 那会,好像还没有虚拟导航栏
-->
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<!--
API 19: 状态栏/虚拟导航栏 半透明,开启后,将以半透明背景的状态浮动在顶/底部
1. 不能与 windowFullscreen=true 同时使用,因为状态直接不显示了,该设置无意义
2. 对于有刘海屏的,状态栏与刘海齐平,启动背景显示在状态栏(包括刘海)下方
3. 不支持 windowDrawsSystemBarBackgrounds 设置,启动背景总是铺满全屏
-->
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<!--
API 21: 状态栏/虚拟导航栏 完全透明
1. 同样的,不能与 windowFullscreen=true 同时使用,理由同上
2. 也不能与 windowTranslucentStatus / windowTranslucentNavigation 同时使用
优先级低于上述属性,同时使用,这里将不生效
3. 对于有刘海屏的,刘海区域也属于启动背景的可用区域,即启动背景铺满全屏
-->
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<!--
API 23 / 27: 状态栏/虚拟导航栏 是否启用浅色模式(指背景浅色),文字为深色
1. windowLightStatusBar 要求 API 23,windowLightNavigationBar 要求 API 27
2. 使用半透明 状态栏/导航栏 ,该设置没什么必要性
3. 使用完全透明的 状态栏/导航栏, 可根据启动背景色调进行设置
-->
<item name="android:windowLightStatusBar">true</item>
<item name="android:windowLightNavigationBar">true</item>
</style>
</resources>
推荐配置
-
/values/styles.xml
中仅设置启动背景就好了,兼容特低版本 -
/values-v19/styles.xml
:Android4.4 以上,支持状态栏半透明
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="android:windowBackground">@drawable/splash</item>
<item name="android:windowTranslucentStatus">true</item>
</style>
</resources>
- 让状态栏完全透明,根据启动背景色调。
如果是深色,/values-v21/styles.xml
: Android5.0 以上,状态栏背景完全透明,文字为浅色
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="android:windowBackground">@drawable/splash</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
如果是浅色,/values-v23/styles.xml
: Android6.0 以上,状态栏背景完全透明,文字为深色
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="android:windowBackground">@drawable/splash</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">true</item>
</style>
</resources>
虚拟导航栏没什么必要去处理,一是因为那里并不影响美观,二是因为全面屏手机越来越多。
网友评论