安卓12+后的系统默认使用Splash配置(icon+背景色)导致重复启动Splash屏幕
如果是老项目会发现在新的原生系统上打开,比如模拟器上,总是会先出现一个icon+白色或者黑色背景色,随后出现我们自己的Splash屏幕。国内的设备大部分没有强制使用,所以不影响,不过有其他Splash问题本文也会给出解决方案,比如全屏幕,透明状态栏,避免home(主页)短暂的闪烁,一般是空的Header或者自定义了Header会出现这个问题。
修复双重Splash的思路
关于双重启动Splash可以参考安卓官方发布:将启动画面实现迁移到 Android 12 及更高版本,由于Expo53以前使用的还是老的自定义Activity作为Splash启动,所以加上新SDK的默认Splash就出现两次Splash。新的API不再使用Activity,而是由系统接管,我们只需要配置style即可,也能更好的适配夜间模式切换。
如果你的项目暂时不考虑大版本跨度升级,我们可以想办法配置系统的启动画面为透明状态,这样可以做到无缝过渡到自定义Splash的状态。有一个其他的方案就是将系统Splash配置设置和自定义的一样,但是这里有个问题就是:如过你的自定义是个广告屏幕或者不是纯色,就很麻烦。比如渐变色的,这个时候状态栏就会很不和谐,很突兀(后面会给出代码,让状态栏后面能够渲染Splash。既然方向确定了,开始写代码。
非Expo项目
如果你是原生的ReactNative项目,在你的android文件目录,打开如下文件编辑:
src…..res….values…
<!-- styles.xml -->
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- 问题就出现在这里,在启动自定义Splash前,先执行系统的启动主题 ,我这里是一个亮色,状态栏文字默认是黑色-->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:textColor">@android:color/black</item>
<item name="android:editTextStyle">@style/ResetEditText</item>
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
<item name="colorPrimary">@color/colorPrimary</item>
<!-- 这里我们加一行配置,状态栏透明状态 -->
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
<style name="ResetEditText" parent="@android:style/Widget.EditText">
<item name="android:padding">0dp</item>
<item name="android:textColorHint">#c8c8c8</item>
<item name="android:textColor">@android:color/black</item>
</style>
<!-- 这里是自定义的Splash -->
<style name="Theme.App.SplashScreen" parent="AppTheme">
<!-- 设置我们自定义的Splash图片 -->
<item name="android:windowBackground">@drawable/splashscreen</item>
<!-- 是否开启渲染在状态栏(后面,不是紧接着下面,是zIndex层级关系的下面),加上之前的透明状态栏这里可以无缝过渡 -->
<item name="android:windowIsTranslucent">true</item>
<!-- 开启亮色的状态栏,黑色文字 -->
<item name="android:windowLightStatusBar">true</item>
</style>
</resources>
设置好Styles后我们要去改系统的默认启动Splash颜色
<!-- colors.xml -->
<resources>
<!-- 这里改透明 -->
<color name="splashscreen_background">@android:color/transparent</color>
<color name="iconBackground">#ffffff</color>
<color name="colorPrimary">#023c69</color>
<color name="colorPrimaryDark">#ffffff</color>
</resources>
Expo Prebuild项目 & Expo Bare项目
如果你是Expo的项目,并且使用了Prebuild,那么需要按照上面RN原生方案改的基础上再修改如下配置,因为Expo Prebuild后会按照app.json配置生成Android目录配置文件,如果你没设置,则是默认的:
<!-- strings.xml -->
<resources>
<string name="app_name">name</string>
<!-- 这里设置cover -->
<string name="expo_splash_screen_resize_mode" translatable="false">cover</string>
<!-- 设置可以让Splash渲染在状态栏Zindex的下面 -->
<string name="expo_splash_screen_status_bar_translucent" translatable="false">true</string>
<!-- 亮色主题 -->
<string name="expo_system_ui_user_interface_style" translatable="false">light</string>
</resources>
由于我们在上面已经将系统Splash设置了透明,所以启动后会无缝过渡到自定义的Splash,同时设置“translucent”,实现FullScreen。如果你使用Expo的app.json配置,有个很可惜的配置项就是Splash背景色不支持设置android的透明格式,Eas会报错,所以Bare工作流需要使用下面的插件自动替换修复这个色值。
在App.config.js or app.json
{
"expo":{
/// ...
"android":{
"statusbar": {
"barStyle": "dark-content",//黑色文字
"translucent": true,//zindex层级:支持Splash在下面,等效上面的values/xml配置
}
}
}
}
为了方面Bare流的开发,我写了一个插件在构建打包的时候同步修改更新,使用这个插件后,不再需要修改prebuild后的文件,也不需要Android目录:
// expo-plugin-transparent-statusbar.js
const {
withAndroidStyles,
AndroidConfig,
withStringsXml,
} = require("@expo/config-plugins");
const { Styles, Strings } = AndroidConfig;
function withTransparentStatusBar(config) {
// 修改 styles.xml
config = withAndroidStyles(config, (config) => {
let styles = config.modResults;
// 覆盖 AppTheme 中已有的 statusBarColor
styles = Styles.assignStylesValue(styles, {
add: true,
parent: { name: "AppTheme" },
name: "android:statusBarColor",
value: "@android:color/transparent",
});
// 添加 Theme.App.SplashScreen 中的 windowIsTranslucent
styles = Styles.assignStylesValue(styles, {
add: true,
parent: { name: "Theme.App.SplashScreen" },
name: "android:windowIsTranslucent",
value: "true",
});
// 添加 Theme.App.SplashScreen 中的 windowLightStatusBar
styles = Styles.assignStylesValue(styles, {
add: true,
parent: { name: "Theme.App.SplashScreen" },
name: "android:windowLightStatusBar",
value: "true",
});
config.modResults = styles;
return config;
});
// 修改 strings.xml
config = withStringsXml(config, (config) => {
config.modResults = Strings.setStringItem(
[
{
$: {
name: "expo_splash_screen_status_bar_translucent",
translatable: "false",
},
_: "true",
},
],
config.modResults,
);
return config;
});
return config;
}
module.exports = withTransparentStatusBar;
插件用法和其他expo插件一样的写法
// app.json > expo > plugins
plugins: [
//...
"./fix-transparent-statusbar.js", //处理android Splash 问题
]
修复导航头Header/自定义Header闪烁
这个问题的解决其实很取巧,不需要代码演示,其实闪烁的原因就是因为Splash过快关闭,在计算自定义的Header高度或者隐藏header的时候有个延迟,只需要将Splash延迟关闭500ms-600ms即可。