一、特定平台代碼
React Native提供了兩種方法來區分平台:
- 使用Platform模塊;
- 使用特定平台擴展名;
1、Platform模塊
React Native提供了一個檢測當前運行平台的模塊;Platform適用於對一小部分代碼需要按照平台定制的情況;
import { Platform, StyleSheet } from "react-native"; const styles = StyleSheet.create({ height: Platform.OS === "ios" ? 200 : 100 });
Platform.OS 在iOS上會返回iOS,而在安卓設備或模擬器上則返回android;還要一個實用的方法是Platform.select()
import { Platform, StyleSheet } from "react-native"; const styles = StyleSheet.create({ container: { flex: 1, ...Platform.select({ ios: { backgroundColor: "red" }, android: { backgroundColor: "blue" } }) } });
1.1 檢測Android版本
在Android上,Version屬性是一個數字,標識Andorid的api level;
import { Platform } from "react-native"; if (Platform.Version === 25) { console.log("Running on Nougat!"); }
1.2 檢測iOS版本
在iOS上,Version屬性是-[UIDevice systemVersion]的返回值,具體形式為一個表示當前系統版本的字符串。如:“10.3”
import { Platform } from "react-native"; const majorVersionIOS = parseInt(Platform.Version, 10); if (majorVersionIOS <= 9) { console.log("Work around a change in behavior"); }
2、特定平台擴展名
當不同平台的代碼邏輯較為復雜時,最好是放到不同的文件里,這時候我們可以使用特定平台擴展名。React Native 會檢測某個文件是否具有.ios.
或是.android.
的擴展名,然后根據當前運行的平台自動加載正確對應的文件。
***.ios.js
***.android.js
二、React Native中使用圖片
1、靜態圖片資源
React Native提供了一個統一的方式來管理iOS和Android應用中的圖片。要往App中添加一個靜態圖片,只需要把圖片放到代碼文件夾某處,就可以直接引用。
<Image source={require('./my-icon.png')} />
如果你有my-icon.ios.png
和my-icon.android.png
,Packager 就會根據平台而選擇不同的文件。
而且還可以使用@2x,@3x這樣的文件名后綴,來為不同的屏幕精度提供圖片。
.
├── button.js
└── img
├── check.png
├── check@2x.png
└── check@3x.png
Packager會打包所有的圖片並且依據屏幕精度提供對應的資源。譬如:iPhone 7 會使用check@2x.png
,而 iPhone 7 plus 或是 Nexus 5 上則會使用check@3x.png
。如果沒有圖片恰好滿足屏幕分辨率,則會自動選中最接近的一個圖片。
這樣會帶來如下的一些好處:
(1)iOS和Android一致的文件系統;
(2)圖片和JS代碼處在相同的文件夾,這樣組件就可以包含自己所用的圖片而不用單獨去設置;
(3)不需要全局命名。不用擔心圖片名字沖突問題。
(4)只有實際被用到(即require)的圖片才會被打包到app。
注意:為了使新的圖片資源機制正常工作,require中的圖片名字必須是一個靜態字符串(不能使用變量,因為require是在編譯時期執行,而非運行時期執行)。通過require方式引用的圖片包含圖片尺寸信息,不需要重新設置width和height.
2、使用混合App的圖片資源
如果你在編寫一個混合 App(一部分 UI 使用 React Native,而另一部分使用平台原生代碼),也可以使用已經打包到 App 中的圖片資源(以拖拽的方式放置在 Xcode 的 asset 類目中,或是放置在 Android 的 drawable 目錄里)。注意此時只使用文件名,不帶路徑也不帶后綴:
<Image source={{uri: 'app_icon'}} style={{width: 40, height: 40}} />
對於放置在 Android 的 assets 目錄中的圖片,還可以使用asset:/
前綴來引用:
<Image source={{uri: 'asset:/app_icon.png'}} style={{width: 40, height: 40}} />
注意:這些做法並沒有任何安全檢查。你需要自己確保圖片在應用中確實存在,而且還需要指定尺寸。
3、Uri數據圖片
有時候你可能拿到的是圖片的 base64 數據,此時可以使用'data:'
格式來顯示圖片。請注意,你需要手動指定圖片的尺寸
。
// 請記得指定寬高! <Image style={{ width: 51, height: 51, resizeMode: 'contain', }} source={{ uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg==', }} />
4、緩存控制(僅iOS)
緩存資源屬性提供了控制網絡層與緩存交互的方式。
- default:使用原生平台默認策略;
- reload:URL的數據將從原始地址加載。不使用現有的緩存數據。
- force-cache:現有的緩存數據將用於滿足請求,忽略其期限或到期日。如果緩存中沒有對應請求的數據,則從原始地址加載。
- only-if-cached:現有的緩存數據將用於滿足請求,忽略其期限或到期日。如果緩存中沒有對應請求的數據,則不嘗試從原始地址加載,並且認為請求是失敗的。
<Image source={{ uri: 'https://facebook.github.io/react/logo-og.png', cache: 'only-if-cached', }} style={{width: 400, height: 400}} />
三、動畫效果的使用
React Native 提供了兩個互補的動畫系統:用於創建精細的交互控制的動畫Animated
和用於全局的布局動畫LayoutAnimation
。
1、Animated
Animated
使得開發者可以非常容易地實現各種各樣的動畫和交互方式,並且具備極高的性能。Animated
旨在以聲明的形式來定義動畫的輸入與輸出,在其中建立一個可配置的變化函數,然后使用簡單的start/stop
方法來控制動畫按順序執行。 Animated
僅封裝了四個可以動畫化的組件:View
、Text
、Image
和ScrollView
,不過你也可以使用Animated.createAnimatedComponent()
來封裝你自己的組件。下面是一個在加載時帶有淡入動畫效果的視圖:
import React from 'react'; import { Animated, Text, View } from 'react-native'; class FadeInView extends React.Component { state = { fadeAnim: new Animated.Value(0), // 透明度初始值設為0 } componentDidMount() { Animated.timing( // 隨時間變化而執行動畫 this.state.fadeAnim, // 動畫中的變量值 { toValue: 1, // 透明度最終變為1,即完全不透明 duration: 10000, // 讓動畫持續一段時間 } ).start(); // 開始執行動畫 } render() { let { fadeAnim } = this.state; return ( <Animated.View // 使用專門的可動畫化的View組件 style={{ ...this.props.style, opacity: fadeAnim, // 將透明度指定為動畫變量值 }} > {this.props.children} </Animated.View> ); } } // 然后你就可以在組件中像使用`View`那樣去使用`FadeInView`了 export default class App extends React.Component { render() { return ( <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}> <FadeInView style={{width: 250, height: 50, backgroundColor: 'powderblue'}}> <Text style={{fontSize: 28, textAlign: 'center', margin: 10}}>Fading in</Text> </FadeInView> </View> ) } }
2、LayoutAnimation
API
LayoutAnimation
允許你在全局范圍內創建
和更新
動畫,這些動畫會在下一次渲染或布局周期運行。它常用來更新 flexbox 布局,因為它可以無需測量或者計算特定屬性就能直接產生動畫。尤其是當布局變化可能影響到父節點(譬如“查看更多”展開動畫既增加父節點的尺寸又會將位於本行之下的所有行向下推動)時,如果不使用LayoutAnimation
,可能就需要顯式聲明組件的坐標,才能使得所有受影響的組件能夠同步運行動畫。
盡管LayoutAnimation
非常強大且有用,但它對動畫本身的控制沒有Animated
或者其它動畫庫那樣方便,所以如果你使用LayoutAnimation
無法實現一個效果,那可能還是要考慮其他的方案。
要在Android上使用 LayoutAnimation,那么目前還需要在UIManager
中啟用:
// 在執行任何動畫代碼之前,比如在入口文件App.js中執行 UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
import React from 'react'; import { NativeModules, LayoutAnimation, Text, TouchableOpacity, StyleSheet, View, } from 'react-native'; const { UIManager } = NativeModules; UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true); export default class App extends React.Component { state = { w: 100, h: 100, }; _onPress = () => { // Animate the update LayoutAnimation.spring(); this.setState({w: this.state.w + 15, h: this.state.h + 15}) } render() { return ( <View style={styles.container}> <View style={[styles.box, {width: this.state.w, height: this.state.h}]} /> <TouchableOpacity onPress={this._onPress}> <View style={styles.button}> <Text style={styles.buttonText}>Press me!</Text> </View> </TouchableOpacity> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', }, box: { width: 200, height: 200, backgroundColor: 'red', }, button: { backgroundColor: 'black', paddingHorizontal: 20, paddingVertical: 15, marginTop: 15, }, buttonText: { color: '#fff', fontWeight: 'bold', }, });