本篇文章主要是简单介绍下 ReactPackage 注册方法中的三个方法的作用和使用。
在0.47.2版本之前,Android原生类如果实现ReactPackage,需要实现
createNativeModules 、 createViewManagers、 createJSModules三个方法,在此之后只需要实现createNativeModules 、 createViewManagers 两个方法。
那么这三个方法的主要作用是什么呢?
createNativeModules 创建原生module,提供原生方法供react native调用
createViewManagers 创建原生View,提供原生View供react native调用
createJSModules 创建JS module, 提供react native方法供原生端调用
下面我们介绍一下这三个方法的具体使用:
createNativeModules
原生Module,注册原生(Android)中继承ReactContextBaseJavaModule的类
方法上添加了@ReactMethod注解的,可以供JS端使用
Android代码 CommonUtilPackage 类
public class CommonUtilPackage implements ReactPackage {
@Override
public List createNativeModules(ReactApplicationContext reactContext) {
List modules = new ArrayList<>();
modules.add(new CommonUtilModule(reactContext));
return modules;
}
public List> createJSModules() {
return Collections.emptyList();
}
@Override
public List createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
Android代码 CommonUtilModule 类
public class CommonUtilModule extends ReactContextBaseJavaModule{
public CommonUtilModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "CommonUtilModule";
}
// 简单的原生吐司方法
@ReactMethod
public void toast(String content){
Toast.makeText(mContext, content ,1).show();
}
}
JS代码
import React from 'react';
import { NativeModules, View, Text } from 'react-native';
export default class App.js extends React.Component{
componentDidMount(){
// 此处未做平台判断只是举例使用
NativeModules.CommonUtilModule.toast('调用原生吐司方法');
}
render(){
return (
WelCome to React Native.
);
}
}
相同的方式,我们看一下react-native包中的Alert是如何实现的
通常我们使用的话
import { Alert } from 'react-native';
// 在某个方法中使用下面这段代码并调用
// iOS和Android上都可用
Alert.alert(
'Alert Title',
'My Alert Msg',
[
{text: 'Ask me later', onPress: () => console.log('Ask me later pressed')},
{text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
{text: 'OK', onPress: () => console.log('OK Pressed')},
],
{ cancelable: false }
)
看RN官网对Alert的定义为(以Android为例)
在 Android 上最多能指定三个按钮,这三个按钮分别具有“中间态”、“消极态”和“积极态”的概念:
如果你只指定一个按钮,则它具有“积极态”的属性(比如“确定”);两个按钮,则分别是“消极态”和“积极态”(比如“取消”和“确定”);三个按钮则意味着“中间态”、“消极态”和“积极态”(比如“稍候再说”,“取消”,“确定”)。
在 Android 上默认情况下点击提示框的外面会自动取消提示框。你可以提供一个额外参数来处理这一事件:{ onDismiss: () => {} }。
还有另外一个参数也可以用来阻止提示框被自动取消,即{ cancelable: false }
API
alert()
static alert(title, message?, buttons?, options?)
react-native包中的实现(以0.48.4版本为例)
实际上,我们的
import { Alert } from 'react-native';
引入的模块为
node_modules/react-native/Libraries/Alert/Alert.js的文件中的Alert类
// 该类中的静态方法
static alert(
title: ?string,
message?: ?string,
buttons?: Buttons,
options?: Options,
type?: AlertType,
): void {
if (Platform.OS === 'ios') {
if (typeof type !== 'undefined') {
console.warn('Alert.alert() with a 5th "type" parameter is deprecated and will be removed. Use AlertIOS.prompt() instead.');
AlertIOS.alert(title, message, buttons, type);
return;
}
AlertIOS.alert(title, message, buttons);
} else if (Platform.OS === 'android') {
AlertAndroid.alert(title, message, buttons, options);
}
}
以Android为例,实际上该方法调用的代码为
AlertAndroid.alert(title, message, buttons, options);
该文件中还有一个AlertAndroid类,存在同样的静态方法,最后调用了
NativeModules.DialogManagerAndroid.showAlert();
根据我们前端调用原生模块的方法,我们找到module名为DialogManagerAndroid的类
路径为:
node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/
dialog/DialogModule.java
再查看showAlert方法,实际上是Android原生(同包下)AlertFragment的show方法去实现该效果的。
createViewManagers
原生模块中提供给JS端使用的自定义View,例如目前RN提供的View、Text、ScrollView etc.
核心掌握: 原生界面 、 属性、CSS样式布局、 事件、 方法
原生界面
1、 创建一个类继承SimpleViewManager
@Override
public String getName() {
return "ViewName";
}
@Override
public View createViewInstance(ThemedReactContext context) {
return new View(context);
}
这里我们创建的类中的createViewInstance方法返回的View,则是我们后面将在RN里面使用的View
2、 注册我们的原生View
在我们实现了Reactpackage的类中的 createViewManagers方法中,注册我们的View
@Override
public List createViewManagers(ReactApplicationContext reactContext) {
return Arrays.asList(new MyViewManager());
}
3、 和我们之前的native module一样,需要注册我们的package
@Override
protected List getPackages() {
return Arrays.asList(
new MyPackage()
);
}
4、 在JS中导出原生组件
// 属性类型
const ViewNameProperty = {
name: 'CloudPlayerView',
propTypes: {
...View.propTypes, //包含默认的View的属性,如果没有这句会报‘has no propType for native prop’错误
},
};
// 导出原生View 原生View名称 、 属性类型
const ViewName = requireNativeComponent('ViewName', ViewNameProperty);
完成以上四步,就能在RN中使用我们Android创建的原生组件了
{ this._assignRoot = ref; }}
/>
属性
在我们继承SimpleViewManager的类中,添加属性方法
1、 添加属性
// name里面的值为 我们对应的RN中的属性名称
@ReactProp(name="title")
public void setTitle(View view,@Nullable String title){
// doSomething
view.setTitle(title);
}
2、 JS中在我们导出组件的 属性里面添加该属性
const ViewNameProperty = {
name: 'ViewName',
propTypes: {
titile: PropTypes.string, // 对应的原生属性
...View.propTypes, //包含默认的View的属性,如果没有这句会报‘has no propType for native prop’错误
},
};
// 导出原生View 原生View名称 、 属性类型
const ViewName = requireNativeComponent('ViewName', ViewNameProperty);
3、 使用
以上三步就完成了一个属性的添加了,可以根据自己的实际事情去添加自己View的属性。
CSS样式布局
研究中,嘻嘻~
事件
1、 注册事件
// 在ViewManager文件中实现该方法
// 注册RN事件 eventName 是事件名称
@Override
public @Nullable Map getExportedCustomDirectEventTypeConstants() {
MapBuilder.Builder builder = MapBuilder.builder();
builder.put(nativeEventName, MapBuilder.of("registrationName", jsEventName));
return builder.build();
}
2、 RN中增加View的事件属性
// 属性类型
const ViewName = {
name: 'ViewName',
propTypes: {
jsEventName: Function, // 增加的事件
...View.propTypes, //包含默认的View的属性,如果没有这句会报‘has no propType for native prop’错误
},
};
3、 RN中添加事件的方法
4、 原生分发事件
/**
* eventEmitter为创建的RCTEventEmitter对象 reactContext.getJSModule(RCTEventEmitter.class)
viewId: 对应的原生View的父类方法的值
nativeEventName: 原生分发的事件名称
*/
eventEmitter.receiveEvent(viewId, type, nativeEventName);
常量
1、 在ViewManager类中实现父类的getExportedViewConstants方法
// 待实践 嘻嘻~
@Override
public @Nullable Map getExportedViewConstants() {
return MapBuilder.of(
"AutoCapitalizationType",
MapBuilder.of(
"none",
0,
"characters",
InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS,
"words",
InputType.TYPE_TEXT_FLAG_CAP_WORDS,
"sentences",
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES));
}
方法
原生UI里面,会存在JS端调用原生UI中的方法的问题;前面我们讲过了事件的用法,事件是通过回调方式,原生端回调传值调用JS的方法,那么方法则是和事件相反,是通过JS端传值主动调用原生端的方法。
下面我们来看下方法是如何使用的:
- 原生端将方法名和方法的id注册一下
// 在我们的ViewManager的类中
// 注册供RN调用的原生方法
@Nullable
@Override
public Map getCommandsMap() {
return MapBuilder.of("native_method_name",1);
}
- 原生端写好JS端调用该方法时在原生端的映射
// 接收RN调用的方法
@Override
public void receiveCommand(View root, int commandId, @Nullable ReadableArray args) { // args为我们在js端传入的参数
switch (commandId){
case 1: // 这里的1和我们之前注册的id对应
// doSomeThing
Log.d("===","RN调用原生UI的方法");
break;
default:
break;
}
}
- JS端调用我们在原生端注册的方法
import { UIManager } from 'react-native';
// 在js端定义的方法
native_method_name = () => {
// 1、ViewName为你在原生创建的ViewManager类的getName()方法返回的值
// 2、 这里传入的null为js端传给原生端的参数
UIManager.dispatchViewManagerCommand(
findNodeHandle(this._assignRoot), UIManager. ViewName.Commands.native_method_name, null);
}
完成以上三步,就可以在原生UI模块里面,js端主动调用原生端的方法了~
createJSModules
JS模块中提供方法给原生端调用的,过程相对繁琐
Android 代码 新增一个 Native 接口,继承 JavaScriptModule 接口
public interface AppModuleInitializer extends JavaScriptModule {
void init(String name);
}
Android 代码 注册该接口
public class AppReactPackage extends MainReactPackage {
@Override
public List> createJSModules() {
List> javaScriptModules = new ArrayList<>();
javaScriptModules.addAll(super.createJSModules());
javaScriptModules.add(AppModuleInitializer.class);
return javaScriptModules;
}
}
JS模块 创建同名的AppModuleInitializer.js 的文件
class AppModuleInitializer {
init(json: string) {
// do something
}
}
AppModuleInitializer = new AppModuleInitializer();
// 注册
BatchedBridge.registerCallableModule(
'AppModuleInitializer',
AppModuleInitializer);
module.exports = AppModuleInitializer;
原生使用方式
AppModuleInitializer initModule = context.getJSModule(AppModuleInitializer.class);
initModule.init("调用JS端代码");
目前该方法在react-native0.47.2中已经被废弃了
有问题可留言,欢迎各大Coder 讨论、指正问题~