React Native封装Android原生控件

第一步:

我们首先要创建一个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

发布了18 篇原创文章 · 获赞 6 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/unhappy_long/article/details/77776059