1. 程式人生 > >React Native封裝Android原生控制元件

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);
    });

遇到的問題:

1.在js端匯出元件後使用的時候,報錯:

這裡寫圖片描述

解決方案:
在requireNativeComponent的第二個引數內,也就是描述元件屬性的時候,在propTypes內,加入…View.propTypes:

這裡寫圖片描述

因為,在這裡我只描述了我規定的屬性欄位,RN其實自己會新增很多預設的屬性,使用擴充套件符合並就可以了。