開發 React Native APP —— 從改造官方Demo開始(二)

通過第一部分開發 React Native APP —— 從改造官方 Demo 開始(一)介紹,App 框架基本構建完成,這部分主要討論 UI/交互、App 發佈前的準備工做及如何發佈,具體內容包括:html

  • 在使用 react navigation 的前提下,iOS 實現單個頁面從下往上(modal)的進入動畫
  • 尺寸自適應
  • 設置啓動頁,更換桌面圖標、app 展現名稱、appID
  • 打包發佈

完整 demo 在這 react-native-complete-demojava

一 擴展 react navigation

這裏的擴展指的是實現可單獨配置頁面的進入方式react navigation 默認只支持全局配置,要麼 card,要麼 modal,配置後全部頁面進入動畫相同)。react

實現上述效果須要作兩方面修改:StackNavigator API(在 route.js 中使用)和進入某個頁面是的調用方式。android

1.1 修改 StackNavigator API

修改後若是使頁面默認狀態爲 card,只須要輸入對應頁面便可,好比 ..navigate('ScreenSome1');若是要使某個頁面進入方式爲 modal 只須要在路徑上加上 Modal 好比:..navigate('ScreenSome2Modal')ios

須要注意的是若是頁面進入方式爲 modal,須要自定義 header,由於默認 header 樣式失效,都疊在一塊了git

/**
 * route.js
 * 自定義 StackNavigator,能夠選擇 screen 進入方式
 */
const StackModalNavigator = (routeConfigs, navigatorConfig) => {
  const CardStackNavigator = StackNavigator(routeConfigs, navigatorConfig);
  const modalRouteConfig = {};
  const routeNames = Object.keys(routeConfigs);

  for (let i = 0; i < routeNames.length; i++) {
    modalRouteConfig[`${routeNames[i]}Modal`] = routeConfigs[routeNames[i]];
  }

  const ModalStackNavigator = StackNavigator(
    {
      CardStackNavigator: { screen: CardStackNavigator },
      ...modalRouteConfig
    },
    {
      // 若是頁面進入方式爲 modal,須要自定義 header(默認 header 樣式失效,都疊在一塊了)
      mode: "modal",
      headerMode: "none"
    }
  );

  return ModalStackNavigator;
};

// 設置路由
const AppNavigator = StackModalNavigator();

1.2 頁面中調用

首先咱們新建頁面 ScreenSome2,接下來就讓它以 modal 的形式進入(從屏幕下面進入),做爲對比 ScreenSome1card 的形式進入(默認進入方式,從屏幕右側進入)。github

由於以 modal 形式進入的頁面須要自定義 header,通常只是一個關閉按鈕,以 ScreenSome2 爲例:segmentfault

/**
 * ScreenSome2/view.js
 * 自定義 header(關閉按鈕)
 */
<View>
  {/* TouchableHighlight 爲關閉按鈕的熱區 */}
  <TouchableHighlight
    onPress={() => self.navigation.goBack()}
    underlayColor="transparent"
    style={{
      display: "flex",
      justifyContent: "center",
      marginTop: pxToDp(30),
      width: pxToDp(150),
      height: pxToDp(90),
      backgroundColor: "yellow"
    }}
  >
    <Text style={{ marginLeft: pxToDp(24) }}>關閉</Text>
  </TouchableHighlight>

  <Text style={{ fontSize: pxToDp(36) }}>some2,以 modal 的形式進入</Text>
</View>

而後就是更改進入 ScreenSome2 代碼,這裏是 ScreenHome 頁面中的代碼:react-native

/**
 * ScreenHome/view.js
 * 自定義 header(關閉按鈕)
 */

{
  /* ScreenSome2 從屏幕右側進入 */
}
<Button
  title="goSome1"
  onPress={() => self.navigation.navigate("ScreenSome1")}
/>;

{
  /* ScreenSome2 從屏幕下面進入 */
}
<Button
  title="goSome2Modal"
  onPress={() => self.navigation.navigate("ScreenSome2Modal")}
/>;

最終效果圖:
自定義頁面進入動畫android-studio

二 自適應

自適應主要包括兩方面:尺寸根據屏幕大小自適應,包括 fontSizewidth 等;圖片分辨率根據屏幕分辨率自適應,也就常說的二倍圖、三倍圖等。

2.1 尺寸自適應

尺寸自適應的原理是經過獲取手機屏幕的寬度,尺寸作相應比例的調整,爲此封裝了一個工具函數,放在了 config/pxToDp.js 中。

調整後的目錄以下:

尺寸轉換函數目錄

  • config/pxToDp.js 尺寸轉換的工具函數
尺寸轉換的工具函數在第一部分 開發 React Native APP —— 從改造官方 Demo 開始(一)已經添加

1)編寫自適應尺寸工具函數

由於全部涉及尺寸的數據都要轉換(fontSizewidth等),因此對轉換後的數據要作處理,保證:1.大於等於 1 的數字向上取整;2.小於 1 的數字,若是是 ios 平臺統一設爲 0.5;若是是安卓平臺統一設爲 1(由於安卓平臺分辨率千差萬別萬別,低分辨率的屏幕顯示 0.5 的尺寸會有鋸齒狀)。工具函數完整代碼以下:

/**
 * pxToDp.js
 * 自適應佈局
 * @param uiElementPx: ui給的原始尺寸
 */

import { Dimensions, Platform } from "react-native";

// app 只有豎屏模式,因此能夠只獲取一次 width
const deviceWidthDp = Dimensions.get("window").width;

// UI 默認給圖是 750
const uiWidthPx = 750;

function pxToDp(uiElementPx) {
  const transferNumb = uiElementPx * deviceWidthDp / uiWidthPx;

  if (transferNumb >= 1) {
    // 避免出現循環小數
    return Math.ceil(transferNumb);
  } else if (Platform.OS === "android") {
    // 若是是安卓,最小爲1,避免邊框出現鋸齒
    return 1;
  }
  return 0.5;
}

export default pxToDp;
實際上,經過 Dimensions.get('window').width 獲取的屏幕寬度和本身想象的可能有出入,好比,iphone7 屏幕 4.7'',獲取到的寬度是 375,華爲 P9 是 5.2',但獲取到的寬度倒是是 360!有點坑,這個工具函數還有待優化。

2)使用自適應尺寸工具函數

使用方法很簡單,在須要轉換單位的組件中將轉換尺寸的工具函數引入,將須要轉換的尺寸傳入工具函數便可,以 ScreenHome 爲例:

/**
 * ScreenHome/view.js
 */

// 引入尺寸轉換工具函數
import pxToDp from "../../config/pxToDp";

// 將須要轉換的單位傳入 pxToDp 中
<Text style={{ fontSize: pxToDp(36) }}>home</Text>;

2.3 圖片分辨率自適應

手機分辨率愈來愈多,尤爲安卓,React Native 能夠根據不一樣分辨率加載不一樣尺寸的圖片,只需在圖片命名上面加以區分。

  • 提供不一樣分辨率的圖片

好比咱們有張圖片叫 test.png,尺寸爲 40 x 40(單位像素),爲了作到自適應屏幕分辨率,咱們還須要提供它的 2 倍圖,3 倍圖,這樣,一張圖片就對應 3 個尺寸,以下:

# 一張圖片提供 3個尺寸

test.png # 尺寸 40 x 40
test@2x.png # 尺寸 80 x 80
test@3x.png # 尺寸 120 x 120

name@nx是 n (n > 1) 倍圖命名規範,React Native 也是根據命名判斷圖片尺寸的。

  • 使用

在引用圖片的時候直接使用 不加倍率後綴的圖片名,好比,直接使用 test.png,以下:

/**
 * ScreenTab3/view.js
 */

<Image
  source={require("../../assets/images/test.png")}
  style={{ height: pxToDp(80), width: pxToDp(80) }}
/>

最終效果圖以下:

  • iphone6: 2 倍圖(圖片放大後能夠看到裏面有 2X 字樣)
  • iphone7Plus:3 倍圖(圖片放大後能夠看到裏面有 3X 字樣)
  • Nexus4:2 倍圖
  • Pixel2:3 倍圖

自定加載不一樣分辨率圖片

三 修改桌面圖標、App 展現名稱,設置啓動頁

修改桌面圖標、App 展現名稱相對簡單,設置啓動頁稍微麻煩。另外,iOS 修改桌面圖標、App 展現名稱,設置啓動頁都須要在 Xcode 中進行。

3.1 設置桌面圖標

由於 App 圖標對應多個尺寸,手動改寫太麻煩,這個網站能夠自動生成 MakeAppIcon

並非全部尺寸的圖片都須要,見下文。
  • iOS

準確點講不能叫設置桌面圖標,而應該是 App 圖標,由於咱們須要設置的不止有桌面展現的圖標,還有設置時 app 圖標、消息推送時 app 圖標,此外若是要發佈到 App store,還須要設置 Apple Store 展現用的 App 圖標。

1)圖片準備

以上不一樣地方用到的 app 圖標尺寸各不相同,具體以下(只針對 iphone,不包括 ipad,iwatch):

尺寸 名稱 用途 是否必須
120x120 Icon-60@2x.png 桌面圖標 (2x) 必須
180x180 Icon-60@3x.png 桌面圖標 (3x) 可選,但推薦設置
80x80 Icon-40@2x.png Spotlight 圖標 (2x) 可選,但推薦設置
120x120 Icon-40@3x.png Spotlight 圖標 (3x) 可選,但推薦設置
58x58 Icon-29@2x.png 設置圖標 (2x) 可選,但推薦設置
87x87 Icon-29@3x.png 設置圖標 (3x) 可選,但推薦設置
40x40 Icon-20@2x.png 通知圖標 (3x) 可選,但推薦設置
80x80 Icon-20@3x.png 通知圖標 (3x) 可選,但推薦設置
1024x1024 iTunesArtwork@2x.png App Store (2x) 必須
名稱不是說必定要和上面相同,但 Icon 、尺寸(如 60)還有倍率(@nx)要有,類型爲 png

2)將圖片拖放至 Xcode 指定位置,具體是:Project Navigator -> Images.xcassets -> AppIcon,以下圖

iOS AppIcon

拖放完成後,經過文件管理器查看項目目錄,也會發現相應圖片。
  • 安卓

安卓的 app 圖標相對簡單,只須要設置桌面圖標。設置位置在 yourApp/android/app/src/main/res/ 目錄下,這個目錄默認有四個文件夾,裏面各對應放置了一種尺寸的桌面圖標圖片,圖片尺寸不一樣,但名稱相同,統一爲 ic_launcher.png,具體以下所示:

文件夾名稱 含義 文件夾內部圖片尺寸 文件夾內部圖片名稱
mipmap-ldpi Low Density Screen 36x36 ic_launcher.png
mipmap-mdpi Medium Density Screen 48x48 ic_launcher.png
mipmap-hdpi High Density Screen 72x72 ic_launcher.png
mipmap-xhdpi Extra-high density screen 96x96 ic_launcher.png
mipmap-xxhdpi xx-high density screen 144x144 ic_launcher.png
mipmap-xxxhdpi xxx-high density screen 192x192 ic_launcher.png

若是你使用了 MakeAppIcon 的服務,直接將對應文件夾所有放入 res/ 目錄下就好,否則就手動替換圖標。

能夠根據實際需求刪除沒必要要的文件,好比,120 DPI 的屏幕不多了,那麼這個文件夾就能夠不要

3.2 修改 App 展現名稱

  • iOS

調出工程設置菜單(雙擊工程名稱或者單擊而後而後右側選擇 Targets --> yourProject),進入 info 選項,在 Custom iOS Target Properties 中添加 Bundle display name,其 value 即是 App 的名稱。具體設置以下圖:

ios修改app名稱

  • 安卓

安卓修改 App 展現名稱在這個文件中 yourApp/android/app/src/main/res/values/strings.xml

strings.xml 這個文件很簡單,所有內容以下:

<resources>
    <string name="app_name">你的app名稱</string>
</resources>

替換 你的app名稱 爲你想要的名字就好。

NOTE:

安卓的話,還要修改默認包名(applicationId),若是不修改,若是系統監測到當前應用的 applicationId 和已安裝的某個應用相同而簽名不一樣,會報錯:「簽名不一致 該應用可能已被惡意篡改」。

在這個文件中修改包名: yourApp/android/app/build.gradle

// ...

defaultConfig {
    applicationId "com.yourAppId"
    // ...
}

// ...

3.3 設置啓動頁

這裏使用了第三方插件react-native-splash-screen,官網教程已經很詳細,這裏作簡要介紹。

  • 項目中安裝依賴

1)下載依賴

yarn add react-native-splash-screen

2)添加到項目中

react-native link react-native-splash-screen

3)在 React Native 配置

這裏指的是設置啓動頁何時消失,下面的代碼是首頁加載完 5s 後啓動頁消失。

/**
 * ScreenHome/index.js
 * 設置啓動頁消失時間
 */
import SplashScreen from "react-native-splash-screen"; // 引入 react-native-splash-screen

export default class ScreenHome extends Component {
  // ...other code
  componentDidMount() {
    // 隱藏啓動頁,若是不設置消失時間,在組件加載完啓動頁自動隱藏
    setTimeout(() => {
      SplashScreen.hide();
    }, 5000);
  }
  // ...other code
}
  • iOS 設置

1)更新 AppDelegate.m

#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import "SplashScreen.h"  // 導入啓動頁插件

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // ...其餘代碼
    [self.window makeKeyAndVisible];
    [SplashScreen show];  // 顯示啓動頁
    return YES;
}

@end

2)準備啓動頁圖片

文件必須是 png 格式的圖片,命名需對應尺寸,可參考下面的命名:

如需自動生成可以使用這個網頁: appicon
尺寸 名稱 用途 是否必須
640 x 960 Default@2x.png iPhone 4 非必須,推薦設置
640 x 1136 Default-568h@2x.png iPhone 5 非必須,推薦設置
750 x 1334 Default-667h@2x.png iPhone 6, 豎屏 必須(必須有至少一個啓動頁圖片)
1242 x 2208 Default-736h@3x.png iPhone 6 Plus, 豎屏 非必須,推薦設置
2208 x 1242 Default-Landscape-736h@3x.png iPhone 6 Plus, 橫屏 非必須,推薦設置
1125 × 2436 Default-812h@3x.png iPhone X, 豎屏 非必須,推薦設置
2436 x 1125 Default-Landscape-812h@3x.png iPhone X, 橫屏 非必須,推薦設置

NOTE:

  • 不少教程給出的啓動頁尺寸比上面的要多,有多是 Xcode 版本不一樣致使,Xcode 9.2,iOS 7+ 只須要上面七個尺寸;
  • 名稱並不是必定要按照上面的要求,直接使用 Default尺寸x尺寸.png 也能夠;

3)在 Xcode 中設置啓動頁

首先新建 LaunchImage 文件,操做步驟以下:

設置啓動頁

而後在 general 設置中將啓動頁指向剛纔新建的 LaunchImage 文件,注意 Launch Screen File 必須爲空,否則就指向 LaunchScreen.xib 中默認的啓動頁了:

設置啓動頁

  • 安卓配置

1)更新 MainActivity.java

import android.os.Bundle; // here
import com.facebook.react.ReactActivity;
// react-native-splash-screen >= 0.3.1
import org.devio.rn.splashscreen.SplashScreen; // here
// react-native-splash-screen < 0.3.1
import com.cboy.rn.splashscreen.SplashScreen; // here

public class MainActivity extends ReactActivity {
   @Override
    protected void onCreate(Bundle savedInstanceState) {
        SplashScreen.show(this);  // here
        super.onCreate(savedInstanceState);
    }
    // ...other code
}

2)新建 launch_screen.xml

app/src/main/res/layout 中建立 launch_screen.xml(若是沒有 layout 目錄,新建),內容以下:

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

3)準備不一樣尺寸的啓動頁圖片並放到項目中

安卓是經過文件夾路徑尋找啓動頁面的,因此,多張尺寸的啓動頁名稱相同,都爲 launch_screen.png,但要放在不一樣文件夾中,文件夾放置目錄爲 yourApp/android/app/src/main/res/,名稱及對應放置的圖片尺寸以下:

文件夾名稱 含義 文件夾內部圖片尺寸 文件夾內部圖片名稱
drawable-ldpi Low Density Screen 240x320 launch_screen.png
drawable-mdpi Medium Density Screen 320x480 launch_screen.png
drawable-hdpi High Density Screen 480x800 launch_screen.png
drawable-xhdpi Extra-high density screen 720x1280 launch_screen.png
drawable-xxhdpi xx-high density screen 960x1600 launch_screen.png
drawable-xxxhdpi xxx-high density screen 1280x1920 launch_screen.png
建議直接從 480x800 起步放置 4 張圖片就好。

4)優化啓動頁出現前的短暫白屏

到這裏,啓動頁功能已經 ok,但若是仔細看,能夠看到啓動頁出現前會有短暫白屏,此時可經過更改android/app/src/main/res/values/styles.xml 解決:

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <!--設置透明背景-->
        <item name="android:windowIsTranslucent">true</item>
    </style>
</resources>
這種方案實際沒有根本解決問題:會發現這樣設置之後點擊圖片不能當即彈出應用,而有短暫的等待時間,待填坑。

5)解決安卓 6.0,7.0 安裝配置完成後出現閃退,參考下面設置:

android/app/src/main/res/values 下面新建 colors.xml 文件,內容以下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- this is referenced by react-native-splash-screen and will throw an error if not defined.  its value does nothing, just here to avoid a runtime error. -->
    <color name="primary_dark">#000000</color>
</resources>

由於 react-native-splash-screen 須要一個名爲 primary_dark 的顏色值做爲狀態欄的顏色。

3.4 最終效果圖

設置完桌面圖標、修改 APP 展現名稱及設置啓動頁以後的效果圖以下:

圖標及啓動頁

四 打包發佈

4.1 安卓打包發佈

  • 生成簽名
keytool -genkey -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000

若是使用 mac,執行該命令的目錄隨意。但必定要保管好本身的 my-release-key.keystore 文件,若是忘記簽名,不能在原有 App 上面升級,只能從新打包發佈。同時,不要將 keystore 文件放入版本控制中。

  • 設置 gradle 變量

1)首先將簽名文件 my-release-key.keystore 放在目錄 yourApp/android/app/

2)修改文件 yourApp/android/gradle.properties 添加下面代碼 (替換 ***** 爲正確的 keystore 密碼、別名、和 key 密碼):

MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=my-key-alias
MYAPP_RELEASE_STORE_PASSWORD=*****
MYAPP_RELEASE_KEY_PASSWORD=*****

3)添加簽名信息到 app 的 gradle 配置中

編輯文件 yourApp/android/app/build.gradle 加入簽名信息

android {
    ...
    defaultConfig { ... }
    signingConfigs {
        release {
            if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
                storeFile file(MYAPP_RELEASE_STORE_FILE)
                storePassword MYAPP_RELEASE_STORE_PASSWORD
                keyAlias MYAPP_RELEASE_KEY_ALIAS
                keyPassword MYAPP_RELEASE_KEY_PASSWORD
            }
        }
    }
    buildTypes {
        release {
            ...
            signingConfig signingConfigs.release
        }
    }
}
  • 打包

在終端輸入下面命令

cd android && ./gradlew assembleRelease

等待構建完成,即可以在 yourApp/android/app/build/outputs/apk/release/app-release.apk 中找到編譯後的發佈版本。

NOTE:若是遇到這個錯誤:Execution failed for task ':app:processReleaseResources',作下述修改:

yourApp/android/gradle.properties 文件最後添加下面代碼:

classpath 'com.android.tools.build:gradle:3.0.0'
distributionUrl=https://services.gradle.org/distributions/gradle-4.1-all.zip
android.enableAapt2=false

若是還有其餘問題,可參考下這篇文章:安卓打包發佈那些坑

4.2 iOS 打包發佈

iOS 打包發佈有些麻煩,對於大多數非 iOS 開發者的限制不是 React Native 自己,而是蘋果自己的機制,好比,必需要有蘋果開發者帳號。iOS 打包發佈打算另寫文章。若是要了解發布流程,能夠參考這兩篇文章:iOS 發佈 App Store 詳細圖文教程React Native iOS 詳細打包步驟

注意一點:打包時 --entry-file 安卓、iOS 是同一個入口文件 index.js,不在區分安卓/iOS

五 小結

到目前位置,從改造官方 demo 開始,一個比較完整的 React Native App 完成了,在此基礎上能夠不斷擴展完善。

固然,從生產角度來講,這個 demo 的完成度不高,好比,不少樣式仍是最原始的狀態、好比 WebView(App 中嵌入 H5)、下拉刷新等也沒有涉及。其中 WebView、下拉刷新等經常使用功能會逐步集成到這個 demo 中,但樣式並不打算作過多優化,由於從使用角度來說,樣式的完成度越高意味着可定製性越差,而且,那樣也會致使代碼的可讀性變差。但願這個 demo 能夠成爲完整、普適但不臃腫的腳手架。

不過,同一套代碼,安卓和 iOS 上展現的樣式會有不一樣,針對這個,會寫文章單獨說明。

參考資料

Choose transition mode for each screen in StackNavigator
React Native 開發適配心得
Apple Developer - App Icon
在模擬器安卓 4.0 上運行正常,在手機上安卓 6.0 7.0 都閃退 不知道什麼狀況求解
Issues with resources generated by react in Android Studio 3
React Native 的默認單位和自適應佈局方案

相關文章
相關標籤/搜索