在React Native中構建啟動屏

發表於2024-03-01
首發於公眾號 前端混合開發,歡迎關注。

在這個教程中,我們將演示如何在React Native中構建一個啟動螢幕。我們將指導你如何使用 react-native-splash-screen 為iOS和Android應用構建出色的歡迎介面。此外,由於Expo很受歡迎,許多人常常選擇使用它,我們也將探討如何在Expo中構建啟動螢幕。

什麼是啟動畫面?

啟動畫面是使用者訪問應用程式其餘功能之前出現的第一個螢幕。可以說,啟動畫面是讓您的移動應用的品牌名稱和圖示深入使用者記憶的最佳方式。

在網路應用中,我們使用預載入器為使用者提供動畫娛樂,同時伺服器操作正在處理中。儘管這聽起來很直接,但它是構建和保留使用者群的關鍵工具。

在React Native中建立啟動屏有很多好處。例如,考慮一個從API載入資料的場景。在使用者等待時顯示載入器是一種良好的使用者體驗。同樣的情況也適用於啟動屏,因為在應用程式啟動時立即顯示載入器可以幫助你在使用者等待應用程式準備就緒時,向他們展示一個有組織的,設計良好的顯示介面。

對於這個 react-native-splash-screen 演示,我們將為AndroidiOS 構建一個啟動螢幕。本教程將指導你如何準備合適的圖片大小,更新必要的檔案,並在應用載入時隱藏啟動螢幕。完成後的應用將如下圖所示

image.png

為什麼啟動畫面的圖片大小很重要

為移動應用建立啟動畫面可能會有些棘手,你肯定不希望由於啟動畫面解析度的不一致在某些裝置上出現顯示問題。例如,安卓裝置的需求與iOS完全不同。大多數有經驗的設計師可以從零開始為兩種裝置建立所需的啟動畫面解析度。

然而,有許多可用的第三方工具可以幫助你為Android和iOS建立啟動螢幕。在這個教程中,我們將使用 App Icon Generator,這是一個用於建立Android和iOS應用圖示和圖片的線上平臺。

在你繼續之前,請確保你有一張高畫質的,2000x2000畫素(72 PPI)的圖片準備好。你可以在GitHub上克隆這些教程的完整原始碼

構建一個React Native啟動螢幕

首先,前往Appicon。將你的圖片拖到提供的框中,然後選擇4x作為你的基礎尺寸。勾選 iOS 和 Android,然後點選生成:

image.png

接下來,解壓下載的檔案,並將 iOS 和 Android 資料夾複製到你克隆的啟動專案的 assets 目錄中的 assets 資料夾裡:

image.png

在React Native 中構建啟動屏需要一些微調。首先,使用下面的任一命令安裝 react-native-splash-screen 包:

/* npm */
npm i react-native-splash-screen --save

/* yarn */
yarn add react-native-splash-screen

為iOS構建一個啟動螢幕

在你的終端中,使用下面的命令連結依賴項:

cd ios // to enter into IOS directory
pod install

接下來,導航到 AppDelegate.m 檔案並用以下程式碼進行更新。新增程式碼 #import "RNSplashScreen" (第6行),並將預設設定為顯示啟動屏 [RNSplashScreen show] (第41行)。請參考下面程式碼中的註釋:

/* ios/SplashScreen/AppDelegate.m */

#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
// Import RNSplashScreen
#import "RNSplashScreen.h"
#ifdef FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>
static void InitializeFlipper(UIApplication *application) {
  FlipperClient *client = [FlipperClient sharedClient];
  SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
  [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
  [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
  [client addPlugin:[FlipperKitReactPlugin new]];
  [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
  [client start];
}
#endif
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
#ifdef FB_SONARKIT_ENABLED
  InitializeFlipper(application);
#endif
  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:@"MySplashScreen"
                                            initialProperties:nil];
  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  // Set the splash screen to show by default.
  [RNSplashScreen show]; 
  return YES;
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
@end

在上述程式碼中,我們對 AppDeligate 檔案做了兩個重要的修改。首先,我們將之前安裝的 RNSplashScreen 匯入到 AppDeligate.m 中。接下來,我們用程式碼 [RNSplashScreen show] 設定 RNSplashScreen 預設顯示。

接下來,在 Xcode 中開啟專案工作區,點選 Images,右鍵點選 Appicon 下方的任意位置,選擇 New Image Set。將圖片名稱設定為“splash”,開啟 assets 資料夾,導航到 iOS 資料夾。將 iOS 中的三張圖片拖到 Xcode 上命名為 1x, 2x 和 3x 的三個框中:

image.png

接下來,選擇 LaunchScreen.storyboard。選擇 View Controller Scene > View Controller > View,點選 SplashScreenPowered by React Native 標籤,並在鍵盤上按 Delete 鍵。

接下來,選擇 View 並點選 Xcode 右上角的尺子圖示。取消選中 Safe Area Layout Guide 選項,點選加號圖示 +,在物件搜尋輸入框中輸入“image view”,然後將“image view”拖到 View 畫布上:

image.png

現在我們已經設定好了影像檢視,點選影像屬性圖示並將影像更改為“splash”。將內容模式設定為“aspect fit”,如下所示:

image.png

更改iOS啟動螢幕顏色

你可能會問的下一個問題是“我如何在 React Native 中更改啟動螢幕的背景顏色?”為了在 iOS 中為啟動螢幕強制使用一致的背景,滾動到背景設定位置並從下拉選單中選擇 Custom。在彈出視窗中,選擇啟動螢幕的期望顏色。在我們的例子中,我們選擇了白色:

image.png

為了確認你的應用可以成功執行,請從Xcode執行一個構建。你應該會看到類似這樣的情況:

image.png

為Android構建啟動螢幕

對於Android,導航到 MainActivity.java 檔案並更新程式碼以使用下面的 react-native-splash-screen 程式碼:

/* android/app/src/main/java/MainActivity.java */

package com.mysplashscreen;
import android.os.Bundle; // Add this here
import com.facebook.react.ReactActivity;
import org.devio.rn.splashscreen.SplashScreen; // Add this here
public class MainActivity extends ReactActivity {
  /**
   * Returns the name of the main component registered from JavaScript. This is used to schedule
   * rendering of the component.
   */
  @Override
  protected String getMainComponentName() {
    return "MySplashScreen";
  }

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        SplashScreen.show(this);  // Add this here
        super.onCreate(savedInstanceState);
    }
}

接下來,在 app/src/main/res/layout 中建立一個名為 launch_screen.xml 的檔案。另外,如果 layout 資料夾不存在,也要建立它:

/* launch_screen.xml */

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/launch_screen" android:scaleType="centerCrop" />
</RelativeLayout>

注意, <ImageView android:src="@drawable/launch_screen" /> 在HTML中等同於 <img src="your_image" /> ,所以請確保用你的自定義圖片的實際名稱替換 launch_screen

然而,Android會自動縮放可繪製的影像,所以你不一定需要為不同的手機尺寸提供圖片。回想一下,我們之前將兩個資料夾(Android和iOS)複製到了我們的資產目錄。這兩個資料夾包含了我們為不同手機密度提供的啟動畫面圖片。

將 Android 目錄中的 drawable folders/assets 複製到可以在 android/app/src/main/res/ 中找到的 res 目錄中。

更改Android的啟動螢幕顏色

要更改Android應用的啟動螢幕背景顏色,請在values資料夾中建立一個名為 colors.xml 的檔案,並複製下面的程式碼:

/* app/src/main/res/values/colors.xml */

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="primary_dark">#000000</color>
</resources>

在上述程式碼中,我們正在建立一個主色,這將是我們首選的背景色。然後,開啟Android Studio中的Android資料夾,開啟AVD,並按照下面的方式執行你的應用程式。如果一切設定正確,你應該會看到類似於這樣的結果:

image.png

在應用載入後隱藏啟動螢幕

為了在應用載入時隱藏啟動螢幕,我們將使用之前安裝的 react-native-splash-screen 包。在你的 App.js 檔案中,複製下面的程式碼:

/* App.js */

import React, {useEffect} from 'react';
import {
  StatusBar,
  StyleSheet,
  SafeAreaView,
  useColorScheme,
} from 'react-native';
import {Colors} from 'react-native/Libraries/NewAppScreen';
import SplashScreen from 'react-native-splash-screen';
import Login from './src/Login';
function App() {
  const isDarkMode = useColorScheme() === 'dark';
  const backgroundStyle = {
    backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
  };
  useEffect(() => {
    SplashScreen.hide();
  }, []);
  return (
    <SafeAreaView style={[styles.container, backgroundStyle]}>
      <StatusBar
        barStyle={isDarkMode ? 'light-content' : 'dark-content'}
        backgroundColor={backgroundStyle.backgroundColor}
      />
      <Login />
    </SafeAreaView>
  );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});
export default App;

在我們的 Login.js 檔案中:

/* Login.js */

import React, {useState} from 'react';
import {
  StyleSheet,
  View,
  Image,
  Text,
  TextInput,
  TouchableOpacity,
} from 'react-native';
import logo from '../assets/skout_logo.png';
export default function Login() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  return (
    <View style={styles.container}>
      <View style={styles.logoView}>
        <Image source={logo} resizeMode="contain" style={styles.logo} />
      </View>
      <View style={styles.inputView}>
        <TextInput
          value={email}
          style={styles.inputText}
          placeholder="+2340899004909"
          placeholderTextColor="#AFAFAF"
          onChangeText={email => setEmail(email)}
        />
      </View>
      <View style={styles.inputView}>
        <TextInput
          value={password}
          style={styles.inputText}
          placeholder="Password"
          placeholderTextColor="#AFAFAF"
          onChangeText={password => setPassword(password)}
        />
      </View>
      <TouchableOpacity style={styles.loginBtn}>
        <Text style={styles.loginText}>LOGIN</Text>
      </TouchableOpacity>
      <View style={styles.actions}>
        <TouchableOpacity style={{marginHorizontal: 15}}>
          <Text style={styles.forgot}>Forgot Password?</Text>
        </TouchableOpacity>
        <TouchableOpacity>
          <Text style={styles.singUp}>Signup</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  logo: {
    fontWeight: 'bold',
    fontSize: 50,
    color: '#fb5b5a',
    marginBottom: 40,
  },
  inputView: {
    width: '80%',
    backgroundColor: '#EAEAEA',
    borderRadius: 25,
    height: 50,
    marginBottom: 20,
    justifyContent: 'center',
    padding: 20,
  },
  inputText: {
    height: 50,
    color: '#777777',
    fontWeight: '800',
  },
  singUp: {
    color: '#39B54A',
    fontWeight: '500',
  },
  loginBtn: {
    width: '80%',
    backgroundColor: '#39B54A',
    borderRadius: 25,
    height: 50,
    alignItems: 'center',
    justifyContent: 'center',
    marginTop: 20,
    marginBottom: 10,
  },
  loginText: {
    color: '#ffffff',
    fontWeight: '800',
  },
  actions: {
    flexDirection: 'row',
    alignItems: 'flex-start',
    justifyContent: 'flex-start',
  },
  logoView: {
    flexDirection: 'row',
    alignItems: 'flex-start',
    marginBottom: 15,
    marginTop: 0,
  },
  logo: {
    marginBottom: 25,
    width: 250,
    height: 100,
  },
});

應用載入後,程式將顯示登入頁面。請參考下面的截圖:

image.png

構建一個Expo啟動螢幕

到目前為止,我們已經探討了如何在一個裸 React Native 應用中構建啟動螢幕。使用 Expo,我們可以以簡化和直接的方式做到這一點,因為 Expo 允許我們在 app.json 檔案中配置我們的啟動螢幕和圖片。

我們將使用上述的 App.jsLogin.js 檔案。這就是我們搭建新專案時 app.json 檔案的樣子:

/* app.json */

{
  "expo": {
    "name": "splash-screen",
    "slug": "splash-screen",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "light",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "assetBundlePatterns": [
      "**/*"
    ],
    "ios": {
      "supportsTablet": true
    },
    "android": {
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#ffffff"
      }
    },
    "web": {
      "favicon": "./assets/favicon.png"
    }
  }
}

如果你觀察上面程式碼中的 splash 值(物件),你會看到我們的啟動影像指向我們的 assets 資料夾,那裡有預設的啟動影像。我們可以用我們的自定義影像替換它。同樣,我們可以調整影像的大小(即 containcoverstretch),以更好地適應我們的螢幕,最後,我們可以根據我們的選擇設定背景顏色。

對於我們的示例,我已經將圖片替換為我們的自定義圖片,然後將背景更改為我們的樣式:

/* app.json */

{
  "expo": {
    ....
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#39B54A"
    },
    ....
}

如你所見,只要我們的應用載入或準備就緒,啟動螢幕就會立即隱藏。為了最佳化使用者體驗,我們可以選擇在隱藏之前顯示啟動螢幕幾秒鐘。為了做到這一點,我們將使用 expo-splash-screen 包,我們可以用以下命令來安裝:

npx expo install expo-splash-screen

接下來,在我們的 App.js 檔案中,我們匯入並呼叫它:

/* App.js */

import * as SplashScreen from 'expo-splash-screen';

SplashScreen.preventAutoHideAsync();
setTimeout(SplashScreen.hideAsync, 5000);

透過這個,我們將看到我們的啟動螢幕持續五秒鐘後才隱藏。這就是結果:

總結

啟動畫面是對任何應用程式的重要補充,因為它在啟動應用程式和顯示主要內容之間創造了平滑的過渡,從而提高了使用者的體驗。啟動畫面有助於強化應用程式的身份,使其容易被使用者識別,從而提高品牌建設。

通常,某些配置和資源(如字型和檢查更新)會在應用準備就緒時立即實施。啟動螢幕有助於在這些資源載入期間讓使用者忙碌,而不是延遲會損害使用者體驗的情況。

交流

首發於公眾號 大遷世界,歡迎關注。📝 每週一篇實用的前端文章 🛠️ 分享值得關注的開發工具 ❓ 有疑問?我來回答

本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

相關文章