這里有很多本地的 UI 部件準(zhǔn)備被用到最新的應(yīng)用程序中 - 其中一些是平臺(tái)的一部分,其他的部分可以作為第三方庫來使用,而且仍然還有更多的部分可能是在你自己的投資組合中使用。React Native 已經(jīng)將幾個(gè)最關(guān)鍵的平臺(tái)組件進(jìn)行了打包,如同 ScrollView 和 TextInput,但是并不是所有都被打包了,所以當(dāng)然也不可能是您以前寫的應(yīng)用程序。幸運(yùn)的是,通過使用 React Native 應(yīng)用程序可以很容易的將現(xiàn)有的組件進(jìn)行無縫集成打包。
就如同本地模塊指南,這是一個(gè)建立在假定你對(duì) Android SDK 編程有些熟悉的基礎(chǔ)上的更高級(jí)的指南。本指南將顯示你該如何構(gòu)建一個(gè)本地的 UI 組件, 幫助你遍歷執(zhí)行可用核心 React Native 庫中可以使用的現(xiàn)有的 ImageViewcomponent 的一個(gè)子集。
在本例中我們將要完全了解實(shí)施要求來實(shí)現(xiàn)在 JavaScript 中允許使用 ImageViews。
本地視圖是由擴(kuò)展 ViewManage 或者更普遍的 SimpleViewManager 所創(chuàng)建和操縱的。在這種情況下 SimpleViewManager 是很方便的,因?yàn)樗m用于普遍的屬性,比如背景顏色、 不透明度和 Flexbox 布局。當(dāng)然也有其他例子,當(dāng)您在使用 FrameLayout 進(jìn)行包裝組件的時(shí)候,那么這時(shí)候您需要使用 ViewManage ,比如 ProgressBar。
這些子類在本質(zhì)上是很單一的 — — 每個(gè)子類之中只有一個(gè)實(shí)例是通過這個(gè)橋接器創(chuàng)建的。他們將本地視圖傳遞到了 NativeViewHierarchyManager 之中,這代表回到了通過使用它們?cè)嫉姆椒▉碓O(shè)置并更新這些必要的視圖的屬性。ViewManagers 通常也是這些視圖的代表,它通過該橋接器將事件發(fā)送回 JavaScript。
傳遞一個(gè)視圖很簡(jiǎn)單:
1.創(chuàng)建 ViewManager 子類
2.使用 @UIProp 注釋視圖屬性
3.執(zhí)行 createViewInstance 方法
4.執(zhí)行 updateView 方法
5.在應(yīng)用程序軟件包中的 createViewManagers 中注冊(cè)管理器
6.執(zhí)行 JavaScript 模塊
ViewManager 子類在本示例中,我們通過繼承 ReactImageView 類型的 SimpleViewManager 來創(chuàng)建的視圖管理器類 ReactImageManager。它是由管理器管理的對(duì)象類型,這將成為一個(gè)本地視圖。通過 getName 返回的名字將被用來從 Javascript 中引用本地視圖類型。
...
public class ReactImageManager extends SimpleViewManager<ReactImageView> {
public static final String REACT_CLASS = "RCTImageView";
@Override
public String getName() {
return REACT_CLASS;
}
我們?cè)?JavaScript 中使用 @UIProp 來注釋需要被反映出來的屬性。目前支持的類型有 BOOLEAN, NUMBER, STRING, MAP 和 ARRAY。每個(gè)屬性都被聲明為公共靜態(tài)最終字符串常量,并且給它們分配的值在 JavaScript 中都會(huì)成為屬性的名稱。
@UIProp(UIProp.Type.STRING)
public static final String PROP_SRC = "src";
@UIProp(UIProp.Type.NUMBER)
public static final String PROP_BORDER_RADIUS = "borderRadius";
@UIProp(UIProp.Type.STRING)
public static final String PROP_RESIZE_MODE = ViewProps.RESIZE_MODE;
createViewInstance 方法我們使用 CreateViewInstance 方法來創(chuàng)建視圖,視圖應(yīng)將其自身初始化到默認(rèn)狀態(tài),然后任何屬性都會(huì)通過后續(xù)調(diào)用 updateView 來進(jìn)行設(shè)置。
@Override
public ReactImageView createViewInstance(ThemedReactContext context) {
return new ReactImageView(context, Fresco.newDraweeControllerBuilder(), mCallerContext);
}
updateView 方法和 iOS 中有些不同的是在 Android 中,不是通過自動(dòng)調(diào)用 setter 方法來給一個(gè)視圖的屬性進(jìn)行賦值; 對(duì)于 Android 而言,你需要通過您的 ViewManager 中的 updateView 方法手動(dòng)調(diào)用 setter。從 CatalystStylesDiffMap 中提取出來值,并且傳遞給視圖實(shí)例。它是通過 updateView 和視圖類的組合來檢查屬性的有效性,并采取相應(yīng)的行動(dòng)。
@Override
public void updateView(final ReactImageView view,
final CatalystStylesDiffMap props) {
super.updateView(view, props);
if (props.hasKey(PROP_RESIZE_MODE)) {
view.setScaleType(
ImageResizeMode.toScaleType(props.getString(PROP_RESIZE_MODE)));
}
if (props.hasKey(PROP_SRC)) {
view.setSource(props.getString(PROP_SRC));
}
if (props.hasKey(PROP_BORDER_RADIUS)) {
view.setBorderRadius(props.getFloat(PROP_BORDER_RADIUS, 0.0f));
}
view.maybeUpdateView();
}
}
ViewManager在 Java 中的最后一步是通過應(yīng)用程序包的成員函數(shù) createViewManagers 在應(yīng)用程序中注冊(cè) ViewManager,這恰巧和 Native Modules 有些相似。
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new ReactImageManager()
);
}
最后一步就是創(chuàng)建 JavaScript 模塊來為您的新視圖的用戶定義 Java 和 JavaScript 之間的連接層。大量工作都是由 Java 和 JavaScript 中的 React 代碼所完成,那么所有留給你的工作就是去描述 propTypes。
// ImageView.js
var { requireNativeComponent } = require('react-native');
var iface = {
name: 'ImageView',
propTypes: {
src: PropTypes.string,
borderRadius: PropTypes.number,
resizeMode: PropTypes.oneOf(['cover', 'contain', 'stretch']),
},
};
module.exports = requireNativeComponent('RCTImageView', iface);
requireNativeComponent 通常具有兩個(gè)參數(shù),第一個(gè)是本地視圖的名稱,第二個(gè)是描述組件接口的對(duì)象。組件接口應(yīng)該聲明一個(gè)友好的名稱在調(diào)試消息中使用,并且必須聲明本地視圖所反映的 propTypes。PropTypes 用于檢查用戶使用本地視圖的有效性。
現(xiàn)在我們知道了如何公開使用 JS 中那些可以輕松控制的本地視圖組件。但是我們?cè)撊绾翁幚碛脩舻氖录?,比如捏拉縮放或平移?當(dāng)本地事件發(fā)生的時(shí)候,本地代碼應(yīng)該把事件傳遞給視圖中的 JavaScript 代表,并且這兩個(gè)視圖都與 getId() 方法返回的值相連接。
class MyCustomView extends View {
...
public void onReceiveNativeEvent() {
WritableMap event = Arguments.createMap();
event.putString("message", "MyMessage");
ReactContext reactContext = (ReactContext)getContext();
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
getId(),
"topChange",
event);
}
}
名字為 topChange 的事件對(duì)應(yīng)于 JavaScript 里面的 onChange 回調(diào) (映射是在 UIManagerModuleConstants.java 里面)。使用原始的事件來調(diào)用此回調(diào)。對(duì)于該事件,我們通常在包裝組件中對(duì)它進(jìn)行加工來形成一個(gè)更簡(jiǎn)單的 API。
// MyCustomView.js
class MyCustomView extends React.Component {
constructor() {
this._onChange = this._onChange.bind(this);
}
_onChange(event: Event) {
if (!this.props.onChange) {
return;
}
this.props.onChange(event.nativeEvent.message);
}
render() {
return <RCTMyCustomView {...this.props} onChange={this._onChange} />;
}
}
MyCustomView.propTypes = {
/**
* Callback that is called continuously when the user is dragging the map.
*/
onChange: React.PropTypes.func,
...
};