您的位置:首页 > Web前端 > React

React Native封装Android原生控件

2017-09-01 18:15 591 查看
第一步:

我们首先要创建一个RN项目:

react-native init 你的项目名


第二步:

是用Android studio打开RN项目中的Android项目。

在新建一个文件夹用于存放我们封装的控件,结构如下:



第三步:

创建ViewManager,比如TextViewManager,代码如下:

import android.graphics.Color;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.RCTEventEmitter;

/**
* Created by sujialong on 2017/9/1.
*/
public class TextViewManager extends SimpleViewManager<TextView> {

@Override
public String getName() {
return "CustomTextView";
}

@Override
protected TextView createViewInstance(ThemedReactContext reactContext) {
final TextView textView = new TextView(reactContext);
//        final ThemedReactContext myContext = reactContext;
//注册点击事件
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
WritableMap event = Arguments.createMap();
event.putString("message", "MyMessage哈哈哈--自定义");
ReactContext reactContext = (ReactContext)textView.getContext();
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
textView.getId(),
"topChange",
event);
}
});
return textView;
}

@ReactProp(name="text")
public void setText(TextView textView,String text){
textView.setText(text);
}

@ReactProp(name="textSize")
public void setTextSize(TextView view,float fontSize){
view.setTextSize(fontSize);
}

@ReactProp(name = "textColor",defaultInt = Color.BLACK)
public void setTextColor(TextView view,int textColor){
view.setTextColor(textColor);
}
}


1.getName方法用于js端导出时,使用的控件名称。

2.createViewInstance方法用于创建控件与初始化状态。

3.使用@ReactProp注解的方法,是将js端传入的属性值导出,并且给控件设置该属性值。

@ReactProp

该注解可以传入两个参数:

1. name:js端使用时传入的参数名,必传。

2. defaultBoolean:设置默认值,可选,其他选项:defaultInt,defaultFloat。这些参数必须是对应的基础类型的值(也就是boolean,int, float),这些值会被传递给setter方法,以免JavaScript端某些情况下在组件中移除了对应的属性。

第四步:

注册ViewManager,新建一个class,名为ReactViewPackage,在这个class里面加入以下代码:

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
* Created by sujialong on 2017/9/1.
*/
public class ReactViewPackage implements ReactPackage {

@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(
new ToastViewManager(reactContext)
);
}

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new TextViewManager()
);
}
}


这里面需要我们实现ReactPackage的两个方法:createViewManagers,createNativeModules。以前还有一个叫做createJSModules的方法,现在被移除掉了。其中,createNativeModules是用来添加原生模块的,比如:Toast等。createViewManagers是用来添加原生的UI组件的。

我们第一个创建的TextView是UI组件,所以我们将TextViewManager添加到createViewManagers中,如果没有引入原生模块,可以将createNativeModules方返回空数组:

@Override
public List<NativeModule> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}


第五步:

在项目的MainApplication.java文件的getPackages方法中添加,我们封装的原生模块:

@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new ReactViewPackage()
);
}


ok,到这一步我们基本的封装已经完毕。

第六步:

需要我们在js端导出所封装的原生组件,如下:

import React, {PureComponent,PropTypes} from 'react';
import {requireNativeComponent,View} from 'react-native';

const CustomTextView = {
name:"CustomTextView",
propTypes:{
"text":PropTypes.string,
"textSize":PropTypes.number,
"textColor":PropTypes.number,
...View.propTypes
}
}

const RCTCustomTextView = requireNativeComponent('CustomTextView',CustomTextView,{
nativeOnly: {onChange: true}
});

export default class MyView extends PureComponent {

_onChange = (event: Event) => {
const onChangeMessage = this.props.onChangeMessage;
onChangeMessage && onChangeMessage(event.nativeEvent);
}

render() {
return (
<RCTCustomTextView {...this.props} onChange={this._onChange}/>
);
}
}

MyView.propTypes = {
onChangeMessage:PropTypes.func,
};


这里,由于我们是封装的TextView,需要使用requireNativeComponent在原生代码中引用。requireNativeComponent通常接受两个参数,第一个参数是原生视图的名字,也就是我们在ViewManager中使用getName方法定义的名字,而第二个参数是一个描述组件接口的对象。组件接口应当声明一个友好的name,用来在调试信息中显示;组件接口还必须声明propTypes字段,用来对应到原生视图上。这个propTypes还可以用来检查用户使用View的方式是否正确。

第六步:

使用所封装好的组件:

import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
DeviceEventEmitter
} from 'react-native';
import CustomTextView from './src/CustomTextView';


export default class CustomRnView extends Component {

render() {
return (
<View style={styles.container}>
<CustomTextView
style={styles.myTextView}
text="我是封装的原生组件"
textSize={15}
onChangeMessage={(msg)=>{
CustomToastView.show("点到我了----",CustomToastView.SHORT);
CustomToastView.getNativeClass(this._getNativeClass);
this._getNativePromise();
}}/>
</View>
);
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
myTextView:{
width:300,
height:100,
},
});


第七步:

在项目的根目录下,运行:

react-native run-android


成功啦!!!



第八步:为原生模块添加方法

要给js端调用的方法,需要使用@ReactMethod注解:

@ReactMethod
public void show(String message, int duration){
Toast.makeText(getReactApplicationContext(), message, duration).show();
}


在js端如下调用:

import CustomToastView from './src/CustomToastView';

CustomToastView.show("message",CustomToastView.SHORT);


第九步:为原生代码添加回调函数

1.使用Callback:

import com.facebook.react.bridge.Callback;

//使用回调函数
@ReactMethod
public void getNativeClass(Callback callback){
callback.invoke("使用回调函数");
}


在js端如下调用:

import CustomToastView from './src/CustomToastView';

CustomToastView.getNativeClass((res) => {
alert(res);
});


2.使用Promise:

//使用promise回调
@ReactMethod
public void getArguments(Boolean isResolve,Promise promise){
WritableMap map = Arguments.createMap();
map.putString("name", "Arno");
map.putString("age", "25");

if(isResolve){
promise.resolve(map);
}else{
promise.reject(map.toString());
}
}


在js端使用:

import CustomToastView from './src/CustomToastView';

CustomToastView.getArguments(true)
.then((res) => {
console.log("getArguments---success");
console.log(res);
},(error)=>{
console.log("getArguments---error");
console.log(res);
});


3.给JS发送事件

private static final String TestEventName = "TestEventName";

//发送事件,js端使用事件监听接收
public void setEvent(){
WritableMap params = Arguments.createMap();
params.putString("name", "Jack");
reactContext_
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(TestEventName, params);
}


js端使用DeviceEventEmitter设置监听接收:

import {
DeviceEventEmitter
} from 'react-native';

DeviceEventEmitter.addListener(CustomToastView.TestEventName,(res)=>{
console.log("我是事件监听");
console.log(res);
});


大家也可以先看一下官方文档:http://reactnative.cn/docs/0.47/native-modules-android.html#content

遇到的问题:

1.在js端导出组件后使用的时候,报错:



解决方案:

在requireNativeComponent的第二个参数内,也就是描述组件属性的时候,在propTypes内,加入…View.propTypes:



因为,在这里我只描述了我规定的属性字段,RN其实自己会添加很多默认的属性,使用扩展符合并就可以了。

最后附上源码:https://github.com/1035901787/CustomRnView
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: