返回

解决React Native闪屏空白:Android与iOS最佳实践

Android

React Native 闪屏空白问题分析与解决

许多React Native开发者在应用启动时,可能会遇到闪屏前出现短暂空白屏幕的情况,这会影响用户体验。本文将分析问题根源,并提供多种解决方案。

常见原因

出现空白屏幕的主要原因是应用初始化加载时间过长,Android和iOS平台在应用启动时都会显示默认的启动屏幕。默认屏幕在React Native环境准备就绪,能够显示自定义闪屏内容前被展现。这个默认启动屏幕一般为白色(或浅色背景),这就是我们看到的空白屏幕。特别当应用体积增大,或者资源加载时间变长时,这种延迟就愈发明显。

解决方案一:配置Launch Screen 或 Splash Screen

此方法的核心是确保原生层在React Native代码执行前,尽快展示启动图片或界面。对于 Android,配置 android/app/src/main/res/drawable/launch_screen.xml;iOS 则使用 Xcode 配置 LaunchScreen 或 Splash Screen。

Android 平台:

  1. 创建 launch_screen.xml 文件:在 android/app/src/main/res/drawable/ 路径下,创建一个名为 launch_screen.xml 的文件。
  2. 定义背景:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/white" />
    <item>
       <bitmap
           android:gravity="center"
           android:src="@mipmap/ic_launcher" />
     </item>
</layer-list>

这里的 @color/white 表示背景色,@mipmap/ic_launcher 为你的应用图标。你可以使用其他的颜色或者图片资源来替换。

  1. 修改 AndroidManifest.xml: 在 android/app/src/main/AndroidManifest.xml 中找到 Activity 的标签,添加或修改 theme 属性。
<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:theme="@style/AppTheme"  
     >
      <activity 
         android:name=".MainActivity"
          android:theme="@style/Theme.App.SplashScreen"   >
            <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
      </activity>

  </application>

android/app/src/main/res/values/styles.xml中,新增如下配置:

  <style name="Theme.App.SplashScreen" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowSplashScreenBackground">@color/white</item>  
       <item name="android:windowSplashScreenAnimatedIcon">@mipmap/ic_launcher</item>
     <item name="postSplashScreenTheme">@style/AppTheme</item>   
    </style>

上述修改中,将Activity主题改为带有闪屏配置的主题,此主题配置了窗口的闪屏背景色和图标, 以及后闪屏应用的主题。

  1. 构建运行:重新编译你的Android应用。

iOS 平台:

  1. 在 Xcode 中选择 Assets.xcassets
  2. 创建一个新的 "Launch Image" 或者 "Launch Screen". 也可以配置 "Launch Storyboard".
  3. 将应用启动的图片或者视图配置在这里,例如添加Logo或者应用名称等信息。
  4. 构建运行:重新编译iOS应用。

解决方案二:使用 expo-splash-screen

Expo 提供了 expo-splash-screen 库,允许你更精细地控制闪屏的显示和隐藏。

  1. 安装 expo-splash-screen 包:

    npx expo install expo-splash-screen
    
  2. 在你的入口文件 (通常是 App.js) 中,使用该库来控制闪屏的隐藏:

import React, { useState, useCallback, useEffect } from 'react';
import { View, Text,StyleSheet } from 'react-native';
import * as SplashScreen from 'expo-splash-screen';


SplashScreen.preventAutoHideAsync();

 const App= () => {
 const [appIsReady,setAppIsReady] = useState(false);
 useEffect(()=>{
   const  prepare =async ()=>{
         try{
            await new Promise(resolve =>setTimeout(resolve,2000)); //mock  data loading time.

         }catch(e){
           console.warn(e);
        }finally{
           setAppIsReady(true)
       }
     };

  prepare();

   },[]);

const onLayoutView= useCallback ( async ()=>{

  if(appIsReady){
    await  SplashScreen.hideAsync()

  }

 },[appIsReady]);

if(!appIsReady){
  return null;
 }
   return(

        <View style={styles.container} onLayout={onLayoutView}>
           <Text>Splash Screen Example</Text>
        </View>
 )
};
const styles = StyleSheet.create({
   container: {
       flex: 1,
      alignItems:"center",
      justifyContent:"center"

   }

});
export default App

这段代码中, SplashScreen.preventAutoHideAsync() 防止了自动隐藏闪屏;只有当我们的应用资源加载完成时,才执行 SplashScreen.hideAsync() 将其隐藏, 使用useEffect 来模拟应用的启动耗时,实际开发中替换成自己的异步操作。 onLayoutView 来确保布局绘制完成后进行闪屏隐藏。
这种方案允许应用在资源加载后,自定义控制闪屏关闭的时机。

解决方案三:自定义加载动画 (高级)

此方案在解决方案二的基础上更近一步,添加自定义的动画。 这就涉及到修改 `SplashScreen`组件内容和相关样式,达到视觉统一的目的。通过此方法实现高度定制化的闪屏。

1. 使用解决方案二 的配置,并确保 `SplashScreen.preventAutoHideAsync()` 放置在应用启动的入口点。

2.在您的App.js或其他根组件中,在应用完成数据加载后通过状态值更改是否渲染真实应用视图:

import React, { useState, useCallback, useEffect } from 'react';
import { View, Text, StyleSheet,ActivityIndicator, Animated } from 'react-native';
import * as SplashScreen from 'expo-splash-screen';


  SplashScreen.preventAutoHideAsync();
  const AnimatedLoader = Animated.createAnimatedComponent(View); // 为动画创建新的view组件

const App = () => {
  const [appIsReady, setAppIsReady] = useState(false);
  const [splashOpacity, setSplashOpacity] = useState(new Animated.Value(1)); // 新增动画控制的初始值


  useEffect(() => {
        const  prepare =async ()=>{
           try{
               await new Promise(resolve =>setTimeout(resolve,2000)); //模拟数据加载.
               
              }catch(e){
                console.warn(e);
             }finally{
                setAppIsReady(true)
              }
         } ;

     prepare();

   }, []);



  const onLayoutView = useCallback(async () => {

        if (appIsReady) {
            // 执行动画然后隐藏SplashScreen.
            Animated.timing(splashOpacity, {
                toValue: 0, //  完全透明
                duration: 300, // 动画时长
                useNativeDriver: true,
           }).start(async ()=>{ //动画完成后才hide。
            await  SplashScreen.hideAsync();

           });

     }
     
     },[appIsReady]);
      if(!appIsReady){ // 应用准备完成之前一直展示splash动画层

        return  <AnimatedLoader  //应用AnimatedLoader作为splash的wrapper
         style={[
                  styles.splashContainer,
                  { opacity: splashOpacity }  // 使用animated 
               ]}>

          <ActivityIndicator size="large" color="blue" />
           </AnimatedLoader> ;


        }



     return(
             <View  style={styles.container} onLayout={onLayoutView}  >
                <Text>SplashScreen动画测试</Text>

               </View>
             )
 };
const styles=StyleSheet.create({
     container:{
          flex:1,
           justifyContent:"center",
         alignItems:'center'
         },
   splashContainer: {
           flex: 1,
         backgroundColor:'white',
           alignItems:"center",
           justifyContent:'center'

     }
  });


export default App;

这个例子中,我们使用 Animated.timing 创建了一个淡出动画。动画在状态变更时启动,并通过修改透明度达到隐藏SplashScreen的效果。

安全提示

确保应用图标和启动界面(包括 launch_screen.xml 文件等)的资源图片放在正确的分辨率和文件夹下,避免在不同设备上的显示不佳问题。在加载资源期间,适当添加 Loading 提示(如 loading动画),增强用户体验。

总而言之,闪屏空白问题可以通过合理的原生层配置、合适的库使用或深度自定义加载方案解决,保证了启动过程流畅和专业。合理选用以上方案,可以有效提升应用的用户体验。