这里有很多本地的 UI 部件准备被用到最新的应用程序中 - 其中一些是平台的一部分,其他的部分可以作为第三方库来使用,而且仍然还有更多的部分可能是在你自己的投资组合中使用。React Native 已经将几个最关键的平台组件进行了打包,如同ScrollView和TextInput,但是并不是所有都被打包了,所以当然也不可能是您以前写的应用程序。幸运的是,通过使用 React Native 应用程序可以很容易的将现有的组件进行无缝集成打包。
就如同本地模块指南,这是一个建立在假定你对 Android SDK 编程有些熟悉的基础上的更高级的指南。本指南将显示你该如何构建一个本地的 UI 组件, 帮助你遍历执行可用核心React Native库中可以使用的现有的 ImageViewcomponent 的一个子集。
ImageView 示例
在本例中我们将要完全了解实施要求来实现在 JavaScript 中允许使用 ImageViews。
本地视图是由扩展ViewManage或者更普遍的SimpleViewManager所创建和操纵的。在这种情况下SimpleViewManager是很方便的,因为它适用于普遍的属性,比如背景颜色、 不透明度和 Flexbox 布局。当然也有其他例子,当您在使用 FrameLayout 进行包装组件的时候,那么这时候您需要使用ViewManage,比如 ProgressBar。
这些子类在本质上是很单一的 — — 每个子类之中只有一个实例是通过这个桥接器创建的。他们将本地视图传递到了NativeViewHierarchyManager之中,这代表回到了通过使用它们原始的方法来设置并更新这些必要的视图的属性。ViewManagers通常也是这些视图的代表,它通过该桥接器将事件发送回 JavaScript。
传递一个视图很简单:
1.创建 ViewManager 子类2.使用@UIProp注释视图属性3.执行createViewInstance方法4.执行updateView方法5.在应用程序软件包中的createViewManagers中注册管理器6.执行 JavaScript 模块
1. 创建ViewManager子类
在本示例中,我们通过继承ReactImageView类型的SimpleViewManager来创建的视图管理器类ReactImageManager。它是由管理器管理的对象类型,这将成为一个本地视图。通过getName返回的名字将被用来从 Javascript 中引用本地视图类型。...public class ReactImageManager extends SimpleViewManager { public static final String REACT_CLASS = "RCTImageView"; @Override
public String getName() { return REACT_CLASS;
}
2. 注释视图属性
我们在 JavaScript 中使用@UIProp来注释需要被反映出来的属性。目前支持的类型有BOOLEAN,NUMBER,STRING,MAP和ARRAY。每个属性都被声明为公共静态最终字符串常量,并且给它们分配的值在 JavaScript 中都会成为属性的名称。@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;
3. 执行createViewInstance方法
我们使用CreateViewInstance方法来创建视图,视图应将其自身初始化到默认状态,然后任何属性都会通过后续调用updateView来进行设置。@Override
public ReactImageView createViewInstance(ThemedReactContext context) { return new ReactImageView(context, Fresco.newDraweeControllerBuilder(), mCallerContext);
}
4. 执行updateView方法
和 iOS 中有些不同的是在 Android 中,不是通过自动调用 setter 方法来给一个视图的属性进行赋值; 对于 Android 而言,你需要通过您的ViewManager中的updateView方法手动调用 setter。从CatalystStylesDiffMap中提取出来值,并且传递给视图实例。它是通过updateView和视图类的组合来检查属性的有效性,并采取相应的行动。@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();
}
}
5. 注册ViewManager
在 Java 中的最后一步是通过应用程序包的成员函数createViewManagers在应用程序中注册 ViewManager,这恰巧和Native Modules有些相似。@Override
public List createViewManagers(
ReactApplicationContext reactContext) { return Arrays.asList( new ReactImageManager()
);
}
6. 执行 JavaScript 模块
最后一步就是创建 JavaScript 模块来为您的新视图的用户定义 Java 和 JavaScript 之间的连接层。大量工作都是由 Java 和 JavaScript 中的 React 代码所完成,那么所有留给你的工作就是去描述propTypes。// ImageView.jsvar { 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通常具有两个参数,第一个是本地视图的名称,第二个是描述组件接口的对象。组件接口应该声明一个友好的名称在调试消息中使用,并且必须声明本地视图所反映的propTypes。PropTypes用于检查用户使用本地视图的有效性。
事件
现在我们知道了如何公开使用 JS 中那些可以轻松控制的本地视图组件。但是我们该如何处理用户的事件呢,比如捏拉缩放或平移?当本地事件发生的时候,本地代码应该把事件传递给视图中的 JavaScript 代表,并且这两个视图都与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的事件对应于 JavaScript 里面的onChange回调 (映射是在UIManagerModuleConstants.java里面)。使用原始的事件来调用此回调。对于该事件,我们通常在包装组件中对它进行加工来形成一个更简单的 API。// MyCustomView.jsclass 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 ;
}
}
MyCustomView.propTypes = { /**
* Callback that is called continuously when the user is dragging the map.
*/
onChange: React.PropTypes.func,
...
};