React Native终于展示的UI全是Native的UI。将Native的信息封装成React方便的调用。

那么Native是怎样封装成React调用的?Native和React是怎样交互的?

ViewManager

UI组件:将Native的UI暴露出来供JS调用。

  • Native组件封装成JS组件供JS调用。这里的一个问题是怎么将Native中的属性用在JS中。以及属性能够有哪些类型的?能够先思考一下。

以下Native的代码自己定义了一个View并定义了一个变化的属性color。

public class MyCustomView extends View {

    private Paint mPaint;

    public MyCustomView(ReactContext context) {
super(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(0xffff0000);
} // 这里相当于能够改变color属性
public void setColor(int color){
mPaint.setColor(color);
invalidate();
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 測试代码。onMeasure中设置的值通过getWidth()/getHeight()拿到的不一样,问题没找到
setMeasuredDimension(300, 300);
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
}
}

创建一个ViewManager。

public class MyCustomViewManager extends SimpleViewManager<MyCustomView> {
protected static final String REACT_CLASS = "MyCustomView"; @Override
public String getName() { // 返回了定义的View Module的名字
return REACT_CLASS;
} @Override
protected MyCustomView createViewInstance(ThemedReactContext reactContext) {
return new MyCustomView(reactContext); // 创建一个View实例供JS使用。 } // 设置属性,一定须要加这个注解,不然不认识
@ReactProp(name = "color")
public void setColor(MyCustomView view, String color) {
view.setColor(Color.parseColor(color));
}
}

创建一个ReactPackage,并在Application中使用。

public class CustomReactPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
} @Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
} // 自己定义的ViewManager都能够加在这里。 @Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new MyCustomViewManager()
);
}
}

在Application中使用ReactPackage。

public class MainApplication extends Application implements ReactApplication {

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
} @Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(), new CustomReactPackage() // 把自己定义的ReactPackage加进来
);
}
}; @Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
} @Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}

将Native组件封装成JS组件。

import React, {
Component,
PropTypes,
} from 'react';
import {
requireNativeComponent,
View,
UIManager,
} from 'react-native'; const ReactNative = require('ReactNative'); // ReactNative通过import没用 export default class MyCustomView extends Component{
constructor(props){
super(props)
} render(){
// {...this.props} 一定须要设置,不让你永远也看不到
return(
<RCTMyCustomView
{...this.props}
</RCTMyCustomView>);
}
} MyCustomView.propTypes = {
color: PropTypes.string, // 设置color属性
...View.propTypes, // 这里一定须要设置,不然会报错。 has no propType for native prop。这个被坑了
}; var RCTMyCustomView = requireNativeComponent('MyCustomView', MyCustomView); // 拿到Native组件

然后就能够愉快的使用了。(最開始没有设置大小,仅仅是在Native的onMeasure中设置了大小,一直没有View出来,被坑了)

// 一定须要设置大小,不然永远看不到。
<MyCustomView
color='#00ff00'
style={{width:300, height:300}}
/>

假设是第一次使用封装UI Component的话。自己一定须要完整的尝试一遍。

  • 交互。

    Native将事件传递给JS、JS将事件传递给Native。想一下这样一个场景,点击了Native以后,JS怎么知道Native被点击了?以及JS是否能告诉Native须要干什么?当然须要了,而且React Native已经封装的非常好了。

在上面的MyCustomViewManager中实现一些方法就能够了。

getCommandsMap()和receiveCommand()用来处理JS向Native发送事件逻辑。getExportedCustomDirectEventTypeConstants()和addEventEmitters()相应了Native向JS发送事件逻辑。

    private static final int CHANGE_COLOR = 1;

    /**
* 能够接收的JS发过来的事件,返回来的数据是一组相应了方法名以及方法相应的一个ID(这个ID须要唯一区分)的Map。
* 这个在进入App的时候就会运行。得到相应的一组Map。
*/
@Nullable
@Override
public Map<String, Integer> getCommandsMap() {
return MapBuilder.of("changeColor", CHANGE_COLOR);
} /**
* 接收JS事件以后的处理。 JS会通过一些发送发送相应的指令过来,Native会由receiveCommand来处理。 * 事件过来时才会运行。 */
@Override
public void receiveCommand(MyCustomView root, int commandId, @Nullable ReadableArray args) {
switch (commandId) {
case CHANGE_COLOR:
root.changeColor();
break;
}
} /**
* 暴露了在JS中定义的方法,比如以下的"onChangeColor"是定义在JS中的方法。
* 这个在进入App的时候就会运行
*
* Returned map should be of the form:
* {
* "onTwirl": {
* "registrationName": "onTwirl"
* }
* }
*/
@Nullable
@Override
public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
return MapBuilder.<String, Object>builder()
.put("changeColor", MapBuilder.of("registrationName", "onChangeColor"))
.build();
} /**
* 发射入口,相当于将Native的一些事件也注冊给JS。 *
* 这个在进入App的时候就会运行。 */
@Override
protected void addEventEmitters(final ThemedReactContext reactContext, final MyCustomView view) {
super.addEventEmitters(reactContext, view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 调用了JS相应的方法。
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher()
.dispatchEvent(new ClickEvent(view.getId()));
}
});
}

在上面的代码中能够看到Native会接受一个1(CHANGE_COLOR)的指令以及会回调一个onChangeColor的方法到JS。那么如今就在JS中实现。把完整的JS代码贴了一遍。凝视也写在了里面。

const ReactNative = require('ReactNative');

const CUSTOM_VIEW = "custom_view";

export default class MyCustomView extends Component{
constructor(props){
super(props) this._onChange = this._onChange.bind(this); // 一定须要这样调用才会把属性绑定过来
} // 把事件给Native
_changeColor() { // is not a function?没有设置this._onChange = this._onChange.bind(this);的时候 let self = this;
UIManager.dispatchViewManagerCommand(
ReactNative.findNodeHandle(self.refs[CUSTOM_VIEW]),
1, // 发送的commandId为1
null
);
} _onChange() {
if (!this.props.handleClick) {
return;
}
this.props.handleClick();
} render(){
// 设置ref,没弄明确为什么一定须要设置ref,大概是_changeColor中的findNodeHandle须要
return(
<RCTMyCustomView
ref={CUSTOM_VIEW}
{...this.props}
onChangeColor={() => this._onChange()}>
</RCTMyCustomView>);
}
} MyCustomView.propTypes = {
handleClick: PropTypes.func,
color: PropTypes.string, // 设置一个属性
...View.propTypes,
}; var RCTMyCustomView = requireNativeComponent('MyCustomView', MyCustomView, {
nativeOnly: {onChangeColor: true}
});

注意上面用到了nativeOnly。有时候有一些特殊的属性,想从原生组件中导出,可是又不希望它们成为相应React封装组件的属性。举个样例,Switch组件可能在原生组件上有一个onChange事件,然后在封装类中导出onValueChange回调属性。这个属性在调用的时候会带上Switch的状态作为參数之中的一个。这种话你可能不希望原生专用的属性出如今API之中。也就不希望把它放到propTypes里。可是假设你不放的话,又会出现一个报错。

解决方式就是带上nativeOnly选项。

(来自 http://reactnative.cn/docs/0.41/native-component-android.html#content)

如今就能够愉快的调用了。

<MyCustomView
ref='view'
color='#00ff00'
handleSizeClick={() => this._handleSizeClick()}
handleClick={() => this._handleClick()}
style={{width:300, height:300}} />

建议刚開始学习的人好好的实践一遍。



最后的结果

NativeModule

Native模块:定义Native的模块供JS调用。

这种场景会比較的多,比方Toast,在JS中没有Toast这类东西。可是Android/IOS中却非经常见。

  • JS调用Native组件

封装一个moudle供JS调用。

注意里面凝视。

@ReactModule(name = "DemoToast")
public class DemoToastModule extends ReactContextBaseJavaModule { private static final String DURATION_SHORT_KEY = "SHORT";
private static final String DURATION_LONG_KEY = "LONG"; public DemoToastModule(ReactApplicationContext reactContext) {
super(reactContext);
} // Module的名称
@Override
public String getName() {
return "DemoToast";
} /**
* 这里定义的值能够被JS中引用。JS引用的时候.SHORT就会相应到相应的Toast.LENGTH_SHORT
*/
@Nullable
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put(DURATION_SHORT_KEY, Toast.LENGTH_SHORT);
constants.put(DURATION_LONG_KEY, Toast.LENGTH_LONG);
return constants;
} /**
* 通过Callback回调到JS
*/
@ReactMethod
public void show(String message, int duration, Callback callback) {
Toast.makeText(getReactApplicationContext(), message, duration).show();
callback.invoke("Egos");
}
}

JS将Native module转化成JS组件。

import { NativeModules } from 'react-native';
RCTDemoToast = NativeModules.DemoToast; // 获取到Native Module var DemoToast = { /**
* 认为这里不是非常好理解,可是这里相应的那个值(SHORT或者LONG)确实
* 是相应了上面Java代码中的getConstants相应的信息。 */
SHORT: RCTDemoToast.SHORT,
LONG: RCTDemoToast.LONG, show(message, duration){
RCTDemoToast.show(message, duration, (msg) => {
var str = msg;
});
}
}; module.exports = DemoToast;
  • 交互。Native回调信息给JS。
    @ReactMethod
public void show(String message, int duration, Callback callback) {
Toast.makeText(getReactApplicationContext(), message, duration).show();
callback.invoke("Egos"); // callback回调信息。 注意上面的RCTDemoToast.show方法第三个參数。
}

在JS中注冊testMethod。Native直接调用JS。

componentWillMount() {
DeviceEventEmitter.addListener('testMethod', (event) => {var s = event;} );
}

以下Native代码发送指令就会运行上面代码。

WritableMap params = Arguments.createMap();
params.putString("xixi","Egos");
sendEvent(getReactApplicationContext(), "testMethod", params);
/**
* 也能够直接发送事件给JS代码
*/
private void sendEvent(ReactContext reactContext,
String eventName, @Nullable WritableMap params) {
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params); // 会回调到上面注冊的testMethod。 }

JavaScriptModule

JS模块:com.facebook.react.CoreModulesPackage中有展示出来一些信息,AppRegistry、RCTEventEmitter(相应了RCTNativeAppEventEmitter)等等。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQTM4MDE3MDMy/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" width="628" height="301">

源代码中定义的JS Module



相当于运行JS代码的时候会相应去运行Native相应的代码。这部分不是View,不是Native Module。这部分内容还不是非常理解,没有找到合适的样例。兴许有问题补充。

思考

  • 查看ReactPackage.java这个类,里面的信息说明了UI组件、Native模块、JS模块这三个信息。也就是我们寻常定义的这三种信息都须要在这里相应的注冊。
public interface ReactPackage {

  /**
* @return list of native modules to register with the newly created catalyst instance
*/
List<NativeModule> createNativeModules(ReactApplicationContext reactContext); /**
* @return list of JS modules to register with the newly created catalyst instance.
*
* IMPORTANT: Note that only modules that needs to be accessible from the native code should be
* listed here. Also listing a native module here doesn't imply that the JS implementation of it
* will be automatically included in the JS bundle.
*/
List<Class<? extends JavaScriptModule>> createJSModules(); /**
* @return a list of view managers that should be registered with {@link UIManagerModule}
*/
List<ViewManager> createViewManagers(ReactApplicationContext reactContext);
}
  • React Native将非常多的Native的UI以及组件都封装成了JS供JS调用,对外暴露的接口应该会越来越全面以及越来越简单,期待未来的发展。
  • 近期用React Native写了一点代码,本来准备写写一些控件的使用以及一些坑,可是想想还是算了。每个控件使用在不同的地方可能就有一些不一样的问题,这些还得花时间慢慢解决。

參考

Native Modules

Native UI Components

React Native中文网

3.React Native在Android中自己定义Component和Module的更多相关文章

  1. React Native For Android 架构初探

    版权声明:本文由王少鸣原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/171 来源:腾云阁 https://www.qclo ...

  2. React Native for Android 学习

    前言 Facebook 在2015.9.15发布了 React Native for Android,把 JavaScript 开发技术扩展到了移动Android平台.基于React的React Na ...

  3. React native 之android的图标和启动图片

    哎哎呀呀,上篇说到了react native的IOS的图标和启动图片的设置,其实最主要的是尺寸!相应的尺寸设定好了以后就不会报错了! ok~这篇说的是React native的android的图标和启 ...

  4. 【React Native开发】React Native For Android环境配置以及第一个实例(1)

    年9月15日也公布了ReactNative for Android,尽管Android版本号的项目公布比較迟,可是也没有阻挡了广大开发人员的热情.能够这样讲在2015年移动平台市场上有两个方向技术研究 ...

  5. React Native for Android 热部署图片自己定义方案

    情景 热部署时,我们期望升级包中包括js代码与图片资源. bundle的热部署网上已经有两种方案了,一种是用反射,一种是利用RN自带函数.将bundle初始化时直接放到指定文件夹下,之后通过替换bun ...

  6. React Native for android 项目驱动教程

    第一节 搭建开发环境 第二节 显示页面标题 第三节 实现页面布局 # React native是什么? React Native,是颠覆性的移动开发技术.它使用js开发,又是原生应用,不同于Hybri ...

  7. React Native for Android应用名及图标修改

    应用开发完了,总不能顶着MyProject和小机器人图标就发布了吧?在发布之前,有多处需要修改的地方.今天我们来全面的看一下 应用ID 俗称PackageName,或APP ID.注意,在gradle ...

  8. react native 之 Android物理返回键

    基本用法 根据文档,安卓back键的处理主要就是一个事件监听: BackAndroid.addEventListener('hardwareBackPress', this.onBackPressed ...

  9. React Native在开发过程中遇到的一些问题(俗称:坑)

    4900 服务器地址错误 运行时产生以下错误:Could not connect to development server. 1.URL地址设置 问题: Could not connect to d ...

随机推荐

  1. eclipse的springMVC环境搭建并输出HelloWorld

    spring简单介绍:https://www.cnblogs.com/package-java/p/10368672.html 1.创建一个Maven Project项目 点击下一步 点击下一步 2. ...

  2. js禁止某个页面的回退

    ;!function(pkg, undefined){ var STATE = 'x-back'; var element; var onPopState = function(event){ eve ...

  3. 【BZOJ4016】【FJOI2014】最短路径树问题

    题意: Description 给一个包含n个点,m条边的无向连通图.从顶点1出发,往其余所有点分别走一次并返回. 往某一个点走时,选择总长度最短的路径走.若有多条长度最短的路径,则选择经过的顶点序列 ...

  4. BZOJ1567 [JSOI2008]Blue Mary的战役地图(二分+二维hash)

    题意 问边长为n的两个正方形中最大的相等子正方形.(n<=50) 题解 用到了二维hash,感觉和一维的不太一样. 对于列行有两个不同的进制数然后也是通过类似前缀和的方法差分出一个矩形的hash ...

  5. springboot 错误页面的配置

    springboot的错误页面,只需在templates下新建error文件夹,将404.500等错误页面放进去即可, springboot会自动去里面查找

  6. Camera Calibration 相机标定:Opencv应用方法

    本系列文章由 @YhL_Leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/49427383 Opencv中Camer ...

  7. ActiveMQ maven

    http://outofmemory.cn/java/mq/apache-activemq-demo

  8. maven手动增加jar文件

    maven手动增加jar文件 在cmd界面输入: mvn install:install-file -Dfile=D:\com.ibm.mq.jar -DgroupId=com.ibm.mq -Dar ...

  9. hadoop-10-创建yum资源库

    hadoop-10-创建yum资源库 1,在/etc/yum.repos.d/下面创建 ambari.repo  HDP.repo  HDP-UTILS.repo 三个文件: [root@server ...

  10. mysql-数据库维护

    一.备份数据 1.使用mysqldump命令备份:前提:musql的版本必须一致. mysqldump -u username -p  --default -character-set=gbk dbn ...