閱讀資訊:
以下文章常會見到 "RN" 字樣,我用來代表 React Native。
此文撰寫時,React Native 的版本為 0.26。
其實我也主要是在讀官方文件而已 XD
這篇文章寫的部分你可以對應到 <歐萊禮 React Native 學習手冊> 的第四章。
樣式
React Native 偏好重複使用有樣式的元件,而非重複使用樣式。p.48 <歐萊禮 React Native 學習手冊 - 使用 JavaScript 打造原生 App.>
- RN 的程式不能跟 React 用在 web 的共用。記得 RN 是 “Learning once, write anywhere”。
- RN 的 componet “樣式 (StyleSheet)” 並沒有繼承的概念,除了樣式要寫在樣式物件 (StyleSheet) 之外,你可以把有樣式的元件包起來。
Image
- 在 RN 引入圖片並不像 web 使用 img 標籤,而是使用 `<Image>`,另外屬性也不是 web img 的 src,而是 source。
- ex: 引入 local 圖片:
<Image source={require('./my-icon.png')}></Image>
- ex: 引入外部圖片:
<Image
source={{ uri: 'http://xxx.xx.xx/xx.png'}}
style={{width: xx, height xx}}></Image>
- 引入外面的圖片需要指定寬高。
- 引入外部的圖片需指定 uri。
- require 指定的路徑,會跟看 Javascript module 的方式一樣 (就先當作是...相對路徑)
- 可針對平台載入同名的檔案,只要在圖檔命名上加入 platform。
my-icon.ios.png
my-icon.android.png
針對不同的螢幕 densities,你可能會有 @2x, @3x, 等其他的後綴檔名圖片,可以這樣擺放:
// 假設這是一個 todo 的 component 資料夾
todo
├── todo.js
└── img
├── check@2x.png
└── check@3x.png
然而在 todo.js 裡面的 Image 可以這樣引入 check 這個圖片
<Image source={require('./img/check.png')} />
- 假若是 iphone5s 則會用到 check@2x (React Native 會知道要用 @2x,你無須擔心這個...,只要把 source 檔名寫好),但在 Nexus 5 會用到 @3x,若沒有適合的圖片,RN 會選最接近的來用。
- 使用 windows 作業系統的人,假如你加入圖片到 project 下,可能需要重啟。
Image 的優點:
- 可同時使用在 ios, android 上。
- Component self-contained. 圖片跟著 component. 跟 js code 放在同一層。
- 沒有 global namespace 的問題。
- 加圖片或是換圖片無須重新編譯,只要重新整理模擬器。
官方建議,在 require 裡面的值,應該要是 statically 的,請看官方的範例:
// GOOD
<Image source={require('./my-icon.png')} />
// BAD
var icon = this.props.active ? 'my-icon-active' : 'my-icon-inactive';
<Image source={require('./' + icon + '.png')} />
// GOOD,把整個 require 的部分抽出來,放到 icon 變數下存取。
var icon = this.props.active ? require('./my-icon-active.png') : require('./my-icon-inactive.png');
<Image source={icon} />
Local Filesystem Image 本地端圖片檔案
這個本機我想指的並不是你的電腦啦,應該是實際在 run 的那隻手機上裡面的圖片。這個部分可以看 CameraRoll 就有範例可以知道如何取得 local resource.
觸控介面
- RN 提供 TouchableHighlight 元件, 以及 PanResponder, Gesture Responder System 兩個低階 API.
- 任何可以被處碰的 component 應該都要有 TouchableHighlight 包裝者(wrapper)。舉例來說,button 是可以被點按的地方,所以用一個 TouchableHighlight 把這個 Button 包起來。
- TouchableHighlight 監聽 onPress 事件 (也有其他事件啦...,要看文件才知),然後你把要 callback 的 function 寫好,等著發生時觸發相對應的動作。
- PanResponder 比 Gesture Responder System 還要高階。
- PanResponder 提供抽象。
TouchableHighlight 範例
如希望監聽某 Image 的 touch 事件 (範例改自官網): 要注意 TouchableHighlight 只能包覆一個 子component 摟!
<TouchableHighlight onPress={this._onPressButton}>
<Image
style={styles.button}
source={require('image!myButton')}
/>
</TouchableHighlight>
Gesture Responder System
- 手機上觸控的行為非常複雜,當然不只有觸碰而已,也有可能多點觸碰或其他 (scrolling, sliding on a widget, or tapping)。而手勢回應就跟 Gesture Responder System 有關。(在官網可看到 Gesture Responder 的詳細)
- Gesture Responder 是處理特定觸碰事件的 View。要成為一個 Gesture Responder 的 View 有幾個一定要填寫的屬性:
(1) View.props.onStartShouldSetResponder: (evt) => true
(2) View.props.onMoveShouldSetResponder: (evt) => true
- 假如其中一個回傳 true (view 會嘗試 (attempts) 成為一個 responder),以下兩個事件也會發生其中一個 (要嘛接受或是拒絕,會發生其中一個):
(3) View.props.onResponderGrant: (evt) => {}
(4) View.props.onResponderReject: (evt) => {}
其實也不只這些,後頭還有很多種 Handlers 可以用。但簡單來說整個 Gesture 的生命週期是: 開始 -> 移動 -> 釋放
。所以你會很常看到 handlers 的名稱含有 start, release, move...等字樣。
Gesture Responder 具有冒泡事件的特性,如果要覆寫這些行為,可在父元件將
View.props.onStartShouldSetResponder, View.props.onMoveShouldSetResponder 回傳 true。
觸控事件發生之後,相關的 handlers 也會被呼叫。被呼叫的時候會接收 evt (nativeEvent) 參數,有很多屬性值可以取得,如觸碰的 ID, 觸碰元素的 x,y 軸座標,時間... 等等。
參考自官方文件:
identifier - The ID of the touch
locationX - The X position of the touch, relative to the element
locationY - The Y position of the touch, relative to the element
pageX - The X position of the touch, relative to the root element
pageY - The Y position of the touch, relative to the root element
target - The node id of the element receiving the touch event
timestamp - A time identifier for the touch, useful for velocity calculation
touches - Array of all current touches on the screen
(source from: https://facebook.github.io/react-native/docs/gesture-responder-
system.html#content)
PanResponder
PanResponder 提供比較高階的 api 處理單點與多點觸控,也能存取原始的事件 (nativeEvent)。他的用法主要是透過 gestureState 可以得到一些你可能有需要的資訊:
- stateID (只要螢幕上有至少一個碰觸)
- moveX, moveY (最近一次觸碰移動的螢幕座標)
- x0, y0
- dx (從碰觸開始累計手勢的距離)
- dy
- vx (目前手勢的向量)
- vy
- numberActiveTouches (螢幕上碰觸的數量)
用法: 建立一個 PanResponder 的物件,你要準備一些 callback function。然後在 render 的方法中使用他。
componentWillMount: function() {
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => this._handlePanResponder,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {},
onPanResponderMove: (evt, gestureState) => {},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {},
onPanResponderTerminate: (evt, gestureState) => {},
onShouldBlockNativeResponder: (evt, gestureState) => {},
});
},
render: function() {
return (
<View {...this._panResponder.panHandlers} />
);
},
結語
- 提供一般元件的觸碰回應,使用 TouchableHighlight。
- 自訂觸碰介面或是較複雜的應用如遊戲類,使用 PanResponder, Gesture Responder System。
參考