说明
本教程根据React Native 中文网的封装原生UI组件文档说明进行完善。
前言
React Native已经封装了大部分最常见的组件,譬如ScrollView和TextInput,但不可能封装全部组件。而且,说不定你曾经为自己以前的App还封装过一些组件,React Native肯定没法包含它们。幸运的是,在React Naitve应用程序中封装和植入已有的组件非常简单。
我们假设你已经对Android编程颇有经验。本教程将会引导你如何封装一个原生UI组件(Button),以及android层和js层如何通信进行实现。
准备工作
在开始之前,我们假设你已经在你本机的React Native开发环境已经搭建好并且能够创建运行一个Demo项目,而且已经安装好Android Studio。
步骤目录
1.创建一个ViewManager的子类。
2.实现createViewInstance方法。
3.导出视图的属性设置器:使用@ReactProp(或@ReactPropGroup)注解。
4.把这个视图管理类注册到应用程序包的createViewManagers里。
5.实现JavaScript模块。
6.android层向js层发送消息。
7.js层向android层发送消息。
封装原生Button组件
1.创建ViewManager的子类
我们创建一个视图管理类RCTButtonManager,它继承自SimpleViewManager<Button>。RCTButtonManager是这个视图管理类所管理的对象类型,这应当是一个自定义的原生视图。重写getName方法返回的名字会用于在JavaScript端引用这个原生视图类型。
public class RCTButtonManager extends SimpleViewManager<Button> {
public static final String REACT_CLASS = "RCTButton";
//这个方法必须实现,用于在JS端根据该名称获取当前对象
@Override
public String getName() {
return REACT_CLASS;
}
}
2.实现createViewInstance方法
private ThemedReactContext mContext;
private Button mBtn;
@Override
protected Button createViewInstance(ThemedReactContext reactContext) {
this.mContext = reactContext;
mBtn = new Button(reactContext);
return mBtn;
}
3.通过@ReactProp(或@ReactPropGroup)注解来导出属性的设置方法
方法的第一个参数是要修改属性的视图实例,第二个参数是要设置的属性值。方法的返回值类型必须为void,而且访问控制必须被声明为public。
/**
* 设置按钮显示文本
* @param btn
* @param text 文本
*/
@ReactProp(name = "text")
public void setSrc(Button btn, String text) {
btn.setText(text);
}
4.注册ViewManager
在Java中的最后一步就是把视图控制器注册到应用中。我们需要创建MyReactPackage类实现ReactPackage接口,然后在createNativeModules方法中添加自己封装的UI组件。如果UI组件没有被注册,它也无法在JS中被访问到。
public class MyReactPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new RCTButtonManager()
);
}
}
并在MainApplication类中注册
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new MyReactPackage()
);
}
5.实现对应的JavaScript模块
创建RCTButton.js
'use strict';
import React,{Component, PropTypes }from 'react';
import {
requireNativeComponent,
View,
UIManager,
findNodeHandle,
} from 'react-native';
var RCT_BUTTON_REF = 'RCTButton';
//通过“RCTButton”获取Android层封装的RCTButton组件,MyButton为下面的定义的类
var RCTButton = requireNativeComponent('RCTButton',MyButton);
//对封装的组件RCTButton组件进行二次封装
class MyButton extends Component{
constructor(props){
super(props);
}
render(){
return <RCTButton
ref = {RCT_BUTTON_REF}
{...this.props}
/>
}
}
MyButton.propTypes = {
text: PropTypes.string,
...View.propTypes,
};
//导出二次封装的RCTButton组件
module.exports = MyButton;
使用封装的RCTButton:
1.引入RCTButton
import RCTButton from './RCTButton'
2.使用
<RCTButton
ref={(RCTButton)=>{this.RCTButton = RCTButton}}
style={{height:42,}}
text="RCTButton"/>
Anroid层向js层发送消息事件
当我们单击按钮时,需要Android层向JS层发送消息回调事件,方便我们针对操作进行相应处理。
1.在RCTButtonManager类中重写getExportedCustomDirectEventTypeConstants方法
//自定义事件名称
private static final String EVENT_NAME_ONCLICK = "onClick";
@Override
public Map getExportedCustomDirectEventTypeConstants() {
return MapBuilder.of(
EVENT_NAME_ONCLICK, MapBuilder.of("registrationName", EVENT_NAME_ONCLICK));
}
2.为mBtn设置单击监听事件
//设置单击监听事件
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//创建携带回调的数据
WritableMap eventData = Arguments.createMap();
eventData.putString("backMsg","你单击了我~");//key用于js中的nativeEvent
//向js层发送回调
mContext.getJSModule(RCTEventEmitter.class).receiveEvent(
mBtn.getId(),//native和js两个视图会依据getId()而关联在一起
EVENT_NAME_ONCLICK,//事件名称
eventData //携带回调的数据,可传入null
);
}
});
3.在RCTButton.js中添加接收回调的函数
我们在java中发送的事件中携带的数据WritableMap中,定义的key与在js中event.nativeEvent.backMsg一致,nativeEvent和key就可以获取到value。
有时候有一些特殊的属性,如onClick和onPress,想从原生组件中导出,但是又不希望它们成为对应React封装组件的属性,可以使用nativeOnly来声明。如果没有什么特殊属性需要设置的话,requireNativeComponent第三个参数可以不用。
下面是需要修改部分的代码(下划线标记处):
var RCTButton = requireNativeComponent('RCTButton',MyButton
,{nativeOnly: {onChange: true}} );
-----------------------------------------------------------------------------
//单击事件回调
_onClick(event){
if(!this.props.onClick){
return;
}
//取出android层回调的数据:event.nativeEvent.key名称
alert(event.nativeEvent.backMsg);
this.props.onClick(event.nativeEvent.backMsg);
}
render(){
return <RCTButton
ref = {RCT_BUTTON_REF}
{...this.props}
onClick={this._onClick.bind(this)}
/>
}
-----------------------------------------------------------------------------
MyButton.propTypes = {
text: PropTypes.string,
onClick:PropTypes.func,
...View.propTypes,
};
最后在使用的RCTButton组件添加onClick事件:
<RCTButton
ref={(RCTButton)=>{this.RCTButton = RCTButton}}
style={{height:42,}}
text="RCTButton"
onClick={(msg)=>{
alert(msg);
}}
/>
Js层向Android层发送消息事件
在某些情况下,我们需要命令封装的UI组件做一些事情,来完善我们的应用。
1.在RCTButtonManager重写getCommandsMap方法
getCommandsMap接收多组命令,每组命令需要包括名称(js端调用的方法名)和命令id,如下面的COMMAND_HELLO_NAME 和 COMMAND_HELLO_ID。
private static final int COMMAND_HELLO_ID = 1;
private static final String COMMAND_HELLO_NAME = "hello";
//定义接收命令
@Override
public Map<String, Integer> getCommandsMap() {
return MapBuilder.of(
COMMAND_HELLO_NAME,COMMAND_HELLO_ID
//更多可以继续在后面追加
// ,COMMAND_?_NAME,COMMAND_?_ID
);
}
2.重写receiveCommand方法,处理相应的命令
//根据接收到的命令处理相关操作
@Override
public void receiveCommand(Button btn, int commandId, @Nullable ReadableArray args) {
switch (commandId){
case COMMAND_HELLO_ID:
if(args != null) {
String name = args.getString(0);//获取第一个位置的数据
Toast.makeText(mContext, String.format("Hello,%s!", name), Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
3.在RCTButton.js中添加hello函数
//这里的函数名可以随意取
hello(name){
//向native层发送命令
UIManager.dispatchViewManagerCommand(
findNodeHandle(this.refs[RCT_BUTTON_REF]),
UIManager.RCTButton.Commands.hello,//Commands.hello与native层定义的COMMAND_HELLO_NAME一致
[name]//命令携带的参数数据,数据形如:["第一个参数","第二个参数",3]
);
}
最后在使用的RCTButton组件中调用hello函数:
<RCTButton
ref={(RCTButton)=>{this.RCTButton = RCTButton}}
style={{height:42,}}
text="RCTButton"
onClick={(msg)=>{
//alert(msg);
this.RCTButton.hello('React Native');
}}
/>
本教程讲述了React Native 封装android原生UI组件Button的整个开发流程,包括设置Button属性、android层向js层之间的相互通信,到此就完成了原生UI组件的开发。
网友评论