react naive封裝原生UI(android篇)

本文對(duì)于UI組件的封裝嘱支,主要從兩部分介紹:

  • 基本視圖封裝
  • 事件處理

demo中是封裝原生Button組件撮躁,記錄使用過程及需要注意的問題。

一牙甫、基本視圖封裝

  • 創(chuàng)建一個(gè)ViewManager的子類滩褥。
  • 實(shí)現(xiàn)createViewInstance方法病蛉。
  • 導(dǎo)出視圖的屬性設(shè)置器:使用@ReactProp(或@ReactPropGroup)注解。
  • 把這個(gè)視圖管理類注冊(cè)到應(yīng)用程序包的createViewManagers里瑰煎。
  • 實(shí)現(xiàn)JavaScript模塊铺然。

1.1創(chuàng)建一個(gè)ViewManager的子類

SimpleViewManager是ViewManager的派生類,通過該類創(chuàng)建和管理視圖對(duì)象酒甸。

public class RCTButton extends SimpleViewManager<Button> {

    private static final String EVENT_NAME_ONCLICK = "onClick";
    private ThemedReactContext mContext;

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

}

SimpleViewManager<Button>中的Button是需要封裝的UI組件魄健,該組件可以是自定義組件。

實(shí)現(xiàn)方法getName插勤,返回值RCTButton在實(shí)現(xiàn)JavaScript模塊時(shí)需要用到诀艰。

1.2實(shí)現(xiàn)createViewInstance方法

RCTButton類中,實(shí)現(xiàn)createViewInstance方法饮六,創(chuàng)建、返回View對(duì)象苛蒲。

@Override
protected Button createViewInstance(ThemedReactContext reactContext) {
    this.mContext = reactContext;
    Button button = new Button(reactContext);
    return button;
}

1.3導(dǎo)出視圖的屬性設(shè)置器

使用@ReactProp(或@ReactPropGroup)注解,這里介紹@ReactProp卤橄。

@ReactProp(name = "text")
public void setText(Button button,String text){
    button.setText(text);
}
  • name="text" : name對(duì)應(yīng)的值是在js代碼中使用該封裝組件時(shí)的屬性名。

1.4 視圖管理類注冊(cè)

//自定義RTCViewPackage
public class RTCViewPackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        return Collections.EMPTY_LIST;
    }

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

實(shí)現(xiàn)了兩個(gè)方法臂外,在createViewManagers方法中添加自定義的視圖管理類RCTButton窟扑。
自定義RTCViewPackage類需要在MainApplication中添加:

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

1.5 實(shí)現(xiàn)JavaScript模塊

'use strict';
import React, { Component } from 'react';

import {
    requireNativeComponent
} from 'react-native';

let RTCBtn = requireNativeComponent("RCTButton",RCTButton);

export default class RCTButton extends Component {

    render() {
        return <RTCBtn {...this.props}/>;
    }

}
  • requireNativeComponent關(guān)聯(lián)js和原生組件喇颁,方法中的兩個(gè)參數(shù):第一個(gè)參數(shù)是JS模塊名稱,第二個(gè)參數(shù)是原生封裝該UI組件時(shí)的名稱嚎货,對(duì)應(yīng)getName返回值橘霎。
  • {...this.props} 必須有,否則組件不顯示殖属。

二姐叁、事件處理

當(dāng)一個(gè)原生事件發(fā)生的時(shí)候,它應(yīng)該也觸發(fā)JS端視圖上的事件洗显,這兩個(gè)視圖會(huì)依據(jù)getId()而關(guān)聯(lián)在一起外潜。(可以理解為原生層View給JS層View發(fā)送消息--個(gè)人理解O(∩_∩)O~)

2.1 事件映射

重寫getExportedCustomDirectEventTypeConstants方法,將原生事件name映射到j(luò)s事件name挠唆,類似于key-value处窥。

public class RCTButton extends SimpleViewManager<Button> {
    private static final String EVENT_NAME_ONCLICK_NATIVE = "nativeClick";
    private static final String EVENT_NAME_ONCLICK_JS = "jsClick";
    @Nullable
    @Override
    public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
        return MapBuilder.<String,Object>builder().put(EVENT_NAME_ONCLICK_NATIVE,MapBuilder.of("registrationName",EVENT_NAME_ONCLICK_JS)).build();
    }
}

2.2 接受事件,傳遞給JS層

在RCTButton類中重寫addEventEmitters方法玄组,添加點(diǎn)擊事件監(jiān)聽滔驾。

    @Override
    protected void addEventEmitters(final ThemedReactContext reactContext, final Button view) {
        super.addEventEmitters(reactContext, view);
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                WritableMap data = Arguments.createMap();
                data.putString("msg","點(diǎn)擊btn2");
                reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
                        view.getId(),
                        EVENT_NAME_ONCLICK,
                        data
                );
            }
        });
    }

關(guān)鍵代碼:reactContext.getJSModule(RCTEventEmitter.class).receiveEvent( view.getId(), EVENT_NAME_ONCLICK_NATIVE, data ); } });
該代碼的類似于:view組件接收到了事件,傳遞給JS層俄讹。
receiveEvent的第一個(gè)參數(shù)是關(guān)聯(lián)原生組件和js組件哆致,第二個(gè)參數(shù)是事件映射中的key值,第三個(gè)參數(shù)是傳遞給js層的數(shù)據(jù)颅悉。

另外沽瞭,如果不重寫addEventEmitters方法,在createViewInstance方法中發(fā)送事件給js端也可剩瓶。但是優(yōu)先級(jí)低于addEventEmitters驹溃。

@Override
    protected Button createViewInstance(final ThemedReactContext reactContext) {
        this.mContext = reactContext;
        final Button button = new Button(reactContext);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //創(chuàng)建數(shù)據(jù)傳遞信使,雷士android中的bundle
                WritableMap data = Arguments.createMap();
                data.putString("msg","點(diǎn)擊了btn");
                mContext.getJSModule(RCTEventEmitter.class).receiveEvent(
                        button.getId(),
                        EVENT_NAME_ONCLICK_NATIVE,//通過該string延曙,查找js層對(duì)應(yīng)的方法名
                        data
                );
            }
        });
        return button;
    }

2.3 js模塊

'use strict';
import React, { Component } from 'react';

import {
    requireNativeComponent
} from 'react-native';

let RTCBtn = requireNativeComponent("RCTButton",RCTButton,{nativeOnly:{jsClick:true}});

export default class RCTButton extends Component {

    _onClick = (event)=>{
        if (!this.props.onPress){
            return;
        }
        //使用event.nativeEvent.msg獲取原生層傳遞的數(shù)據(jù)
        this.props.onPress(event.nativeEvent.msg);
    }

    render() {
        return <RTCBtn
            {...this.props} jsClick = {(event)=>this._onClick(event)}/>;
    }

}

關(guān)鍵代碼:

  • {nativeOnly:{jsClick:true}} : 故名思議豌鹤,nativeOnly--只在原生中生效的屬性,該屬性將不再propType中定義枝缔,若不添加編譯時(shí)將會(huì)報(bào)錯(cuò)布疙。
  • jsClick = {(event)=>this._onClick(event)} :通過event可以獲取到原生傳遞的數(shù)據(jù)
  • jsClick 對(duì)應(yīng)原生代碼中的事件映射中的private static final String EVENT_NAME_ONCLICK_JS = "jsClick"

2.4 使用JS模塊**

render() {
    return (
      <View style={styles.container}>
        <RCTButton text="原生btn" style = {{width:80,height:40}} onPress = {
          (msg)=>{
            alert("原生傳遞的數(shù)據(jù)為:"+msg)}}/>
      </View>
    );
  }

使用過程中,添加了onPress屬性愿卸。

拓展知識(shí):

1.原生發(fā)送消息灵临,js層注冊(cè)監(jiān)聽

reactContext
            .getJSModule(RCTNativeAppEventEmitter.class)
            .emit(eventName, params);
           //eventName:String JS中通過eventName接收  
           //params:WritableMap 需要傳遞的參數(shù) 

js層接收:

NativeAppEventEmitter.addListener('onVolumeChange', () => {})
//onVolumeChange =>原生代碼中的eventName

2.自定義組件時(shí),js層給native層發(fā)送消息
native層需重寫getCommandsMapreceiveCommand方法趴荸。

/**  
 * 接收交互通知  
 * @return  
 */  
@Nullable  
@Override  
public Map<String, Integer> getCommandsMap() {  
    return MapBuilder.of("handleTask",1);  
}  
//根據(jù)RN層發(fā)送的對(duì)應(yīng)通知ID儒溉,處理對(duì)應(yīng)任務(wù)請(qǐng)求
@Override  
public void receiveCommand(ImageView root, int commandId, @Nullable ReadableArray args) {  
    switch (commandId){  
        case 1:  
            if(args != null) {  
                String name = args.getString(0);//獲取第一個(gè)位置的數(shù)據(jù)  
                Toast.makeText(context, "收到RN層的任務(wù)通知...", Toast.LENGTH_SHORT).show();  
            }  
            break;  
        default:  
            break;  
    }  
}  

js層發(fā)送任務(wù)通知

import {  UIManager, findNodeHandle } from 'react-native';

sendNotification() {  
  //向native層發(fā)送命令  
  UIManager.dispatchViewManagerCommand(  
      findNodeHandle(this.refs.rctBtn),  
      UIManager.RCTButton.Commands.handleTask, // Commands后面的值與原生層定義的`handleTask`一致  
      ["from React Native"] // 向原生層傳遞的參數(shù)數(shù)據(jù),數(shù)據(jù)形如:["第一個(gè)參數(shù)","第二個(gè)參數(shù)",...]  
  );  
}  
render() {
        return <RTCBtn
            ref = 'rctBtn'
            {...this.props} onClick = {(event)=>this._onClick(event)}/>;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市发钝,隨后出現(xiàn)的幾起案子顿涣,更是在濱河造成了極大的恐慌波闹,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涛碑,死亡現(xiàn)場(chǎng)離奇詭異精堕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蒲障,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門歹篓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人晌涕,你說(shuō)我怎么就攤上這事滋捶。” “怎么了余黎?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵重窟,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我惧财,道長(zhǎng)巡扇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任垮衷,我火速辦了婚禮厅翔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘搀突。我一直安慰自己刀闷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布仰迁。 她就那樣靜靜地躺著甸昏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪徐许。 梳的紋絲不亂的頭發(fā)上施蜜,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音雌隅,去河邊找鬼翻默。 笑死,一個(gè)胖子當(dāng)著我的面吹牛恰起,可吹牛的內(nèi)容都是我干的修械。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼检盼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼祠肥!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤仇箱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后东羹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剂桥,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年属提,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了权逗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡冤议,死狀恐怖斟薇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情恕酸,我是刑警寧澤堪滨,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站蕊温,受9級(jí)特大地震影響袱箱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜义矛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一发笔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凉翻,春花似錦了讨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至艇挨,卻和暖如春残炮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缩滨。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工势就, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人脉漏。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓苞冯,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親侧巨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子舅锄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容