React Native 开发中不可避免涉及到到同原生交互通讯,FB 官方封装了一系列的跨平台组件,但是总是不能面面俱到的,所以很多组件还是需要我们自己去封装,实现同原生层交互。
Android自定义扩展包括两部分,一个是 module,一个是 View,这两个部分 js 层同其交互方法有所不同。
1. RN 同 Module 通讯
步骤一:创建一个 BaseModule 的抽象类,BaseModule 继承自ReactContentBaseJavaModule,然后在内部定义一个方法sendEvent,用来向js层发送数据,当其它 Android 模块继承 BaseModule,可以直接调用 sendEvent 方法,发送数据给 js 层,js 只需要监听对应方法名,就可以获得发送过来的值。
BaseModule 代码:
package com.test;
import android.support.annotation.Nullable;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
abstract public class BaseModule extends ReactContextBaseJavaModule {
protected ReactApplicationContext context;
public BaseModule(ReactApplicationContext reactContext) {
super(reactContext);
context = reactContext;
}
/**
* 原生层向js层发送数据
* @param eventName
* @param params
*/
protected void sendEvent(String eventName,@Nullable WritableMap params) {
context
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
}
步骤二:然后创建一个 Android 模块继承 BaseModule,使用 @ReactMethod 来标记那些你希望通过 Js 来访问的方法。
自定义 Module 代码:
package com.test;
import com.facebook.react.bridge.ReactApplicationContext;
public class TestModule extends BaseModule {
public TestModule(ReactApplicationContext reactContext) {
super(reactContext);
context = reactContext;
}
@Override
public String getName() {
return "Test";
}
@ReactMethod
public void test(String text) {
WritableMap event = Arguments.createMap();
event.putString("text", text);
sendEvent("testEvent",event);
}
}
步骤三:最后在 js 层调用 native 层方法,并监听 native 返回。一般写第三方模块的话会写一个 index.js 文件,统一实现这些方法。使用DeviceEventEmitter.addListener监听native层返回,若只是想触发一次监听,那可以使用DeviceEventEmitter.once。
import {
NativeModules,
DeviceEventEmitter
} from 'react-native';
const listeners = {};
const TestModule = NativeModules.Test;
export default class Test {
static test(text) {
TestModule.test(text);
}
static addTestListener(cb) {
listeners[cb] = DeviceEventEmitter.addListener('testEvent', resp => {
cb(resp);
});
}
static removeTestListener(cb) {
if (!listeners[cb]) {
return;
}
listeners[cb].remove();
listeners[cb] = null;
}
}
2. RN 同 View 通讯
步骤一: 继承自ViewGroupManager,使用 @ReactProp(name = “xx”) 来传递props值到native层中,如果想通过方法从 js 层传值过来,就需要在 native 层重写 getCommandsMap 和 receiveCommand,在 receiveCommand 通过 type 值获得从 js 层不同调用传递过来的数据 。
package com.test;
import android.support.annotation.Nullable;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.facebook.react.views.view.ReactViewGroup;
import java.util.Map;
/**
* Created by chenwenyu on 17-8-27.
*/
public class TestViewManager extends ViewGroupManager<MyCustomView> {
private ThemedReactContext mReactContext;
public static final int UPDATE_DATA = 1;
@Override
public String getName() {
return "TestView";
}
@Override
protected MyCustomView createViewInstance(ThemedReactContext reactContext) {
mReactContext = reactContext;
return new MyCustomView(reactContext);
}
@Override
public Map<String, Integer> getCommandsMap() {
return MapBuilder.of(
"updateData",
TestViewManager.UPDATE_DATA
);
}
@Override
public void receiveCommand(MyCustomView view, int commandType, @Nullable ReadableArray args) {
switch (commandType) {
case TestViewManager.UPDATE_DATA:
updateData(view, args.getMap(0));
break;
default:
throw new JSApplicationIllegalArgumentException(String.format(
"Unsupported commadn %d received by $s",
commandType,
this.getClass().getSimpleName()
));
}
}
@ReactProp(name = "visibility")
public void setVisibility(ReactViewGroup reactViewGroup, int visibility) {
reactViewGroup.setVisibility(visibility);
}
/**
* 原生层向js层发送数据
* @param eventName
* @param params
*/
private void sendEvent(MyCustomView myCustomView, String eventName, @Nullable WritableMap params) {
WritableMap event = Arguments.createMap();
event.putMap("params", params);
event.putString("type", eventName);
mReactContext
.getJSModule(RCTEventEmitter.class)
.receiveEvent(myCustomView.getId(),
"topChange",
event);
}
private void updateData(MyCustomView myCustomView, ReadableMap option) {
if (option != null) {
String data = option.getString("data");
data += "data:" + data;
WritableMap writableMap = Arguments.createMap();
writableMap.putString("callData",data);
sendEvent(myCustomView,"test",writableMap);
}
}
}
步骤二:js 层调用,可以通过直接设置 props 的值向原生层传递值,也可以通过 dispatchViewManagerCommand 传递值。
import {
requireNativeComponent
} from 'react-native';
import React, {
PureComponent
} from 'react';
import react_native from 'react-native';
var RCTTestView = react_native.UIManager.TestView;
var Commands = RCTTestView.Commands;
var COMMAND_UPDATE_DATA = Commands.updateData; //同原生层getCommandsMap相对应
export default class TestView extends PureComponent {
constructor() {
super();
}
_onChange(event) {
if (typeof this.props[event.nativeEvent.type] === 'function') {
this.props[event.nativeEvent.type](event.nativeEvent.params);
}
}
updateData(data) {
react_native.UIManager.dispatchViewManagerCommand(react_native.findNodeHandle(this), COMMAND_UPDATE_DATA, [data]);
}
render() {
return <TestShowView visibility={1} onChange={this._onChange.bind(this)}/>;
}
}
const TestShowView = requireNativeComponent('TestView', TestView, {
nativeOnly: {
onChange: true
}
});
当了解了 js 层同 Android 原生层通讯之后,你就可以自己扩展封装一些组件供自己所用了。