1. 程式人生 > >react-native呼叫Android原生UI元件

react-native呼叫Android原生UI元件

當react-native的UI元件不能滿足需求時,可以考慮在原生自定UI元件,讓RN呼叫.使用原生UI所考慮的問題:

一.原生UI被呼叫;

二.修改原生UI屬性值;

三.捕捉原生UI的響應;

四.RN向原生UI元件發訊息;

下面貼上程式碼,逐步分析,實現:

1.在原生裡自定義UI,建立本地模組封裝.

 

package com.firstapp.widget;

 

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Paint;

import android.util.Log;

import android.view.View;

 

import com.facebook.react.uimanager.PixelUtil;

 

/**

* Description:

* Created by song on 2018/7/23.

* email:[email protected]

*/

public class CircleView extends View {

private final String TAG = "CircleView";

private Paint mPaint;

private float mRadius;

public CircleView(Context context) {

super(context);

mPaint = new Paint();

}

/**

* 設定圓的半徑

* @param color

*/

public void setColor(Integer color) {

mPaint.setColor(color); // 設定畫筆顏色

invalidate(); // 更新畫板

}

/**

* 設定圓的半徑

* @param radius

*/

public void setRadius(Integer radius) {

mRadius = PixelUtil.toPixelFromDIP(radius);

invalidate();

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);

Log.d(TAG, "繪製一個半徑為100px的圓");

}

public void onReciveNativeEvent(){

 

}

}

2 : 建立ViewManager 繼承 SimpleViewManager<CircleView>

package com.firstapp.widget.manager;

 

import android.util.Log;

import android.view.View;

import android.widget.Toast;

 

import com.facebook.react.bridge.Arguments;

import com.facebook.react.bridge.LifecycleEventListener;

import com.facebook.react.bridge.ReadableArray;

import com.facebook.react.bridge.WritableMap;

import com.facebook.react.common.MapBuilder;

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;

import com.firstapp.widget.CircleView;

 

import java.util.Map;

 

import javax.annotation.Nullable;

 

/**

* Description:

* Created by song on 2018/7/23.

* email:[email protected]

*/

public class CircleViewManager extends SimpleViewManager<CircleView> implements LifecycleEventListener {

private static final String EVENT_NAME_ONCLICK = "onClick";

private static final String HANDLE_METHOD_NAME = "handleTask"; // 互動方法名

private static final int HANDLE_METHOD_ID = 1; // 互動命令ID

private ThemedReactContext mContext;

/**

* 設定js引用名

*/

@Override

public String getName() {

return "MCircle";

}

 

/**

* 建立UI元件例項

*/

@Override

protected CircleView createViewInstance(ThemedReactContext reactContext) {

this.mContext = reactContext;

this.mContext.addLifecycleEventListener(this);

final CircleView circleView = new CircleView(reactContext);

circleView.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

WritableMap data = Arguments.createMap();

data.putString("msg","native UI被點了");

mContext.getJSModule(RCTEventEmitter.class).receiveEvent(

circleView.getId(), // RN層原生層根據id繫結在一起

EVENT_NAME_ONCLICK, // 事件名稱

data // 傳遞的資料

);

}

});

 

return circleView;

}

 

/**

* 設定背景色

*/

@ReactProp(name = "color")

public void setColor(CircleView view, Integer color) {

view.setColor(color);

}

 

 

/**

* 設定半徑

*/

@ReactProp(name = "radius")

public void setRadius(CircleView view, Integer radius) {

view.setRadius(radius);

}

 

 

/**

* 自定義事件

*/

@Nullable

@Override

public Map getExportedCustomDirectEventTypeConstants() {

return MapBuilder.of(EVENT_NAME_ONCLICK, MapBuilder.of("registrationName",EVENT_NAME_ONCLICK));

}

 

/**

* 接收互動通知

* */

@Nullable

@Override

public Map<String, Integer> getCommandsMap() {

return MapBuilder.of(HANDLE_METHOD_NAME,HANDLE_METHOD_ID);

}

 

/**

* 處理通知

* */

@Override

public void receiveCommand(CircleView root, int commandId, @Nullable ReadableArray args) {

switch (commandId){

case HANDLE_METHOD_ID:

Log.d("ACCEPT========","come here");

Toast.makeText(mContext,"RN層任務通知",Toast.LENGTH_LONG).show();

if (args!=null){

// String message=args.getString(0); //獲取

// Toast.makeText(mContext,"RN層任務通知",Toast.LENGTH_LONG).show();

}

break;

default:

break;

}

}

 

@Override

public void onHostResume() {

 

}

 

@Override

public void onHostPause() {

 

}

 

@Override

public void onHostDestroy() {

 

}

}

RN與原生UI的互動邏輯在ViewManager處理,相關方法的使用,註釋已說明.

 

3.建立本地模組包.

package com.firstapp.widget.manager;

 

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;

 

/**

* Description:

* Created by song on 2018/7/23.

* email:[email protected]

*/

public class CirclePackage implements ReactPackage {

@Override

public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {

return Collections.emptyList();

}

 

@Override

public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {

return Arrays.<ViewManager>asList(

new CircleViewManager()

);

}

}

 

4.在Application新增自定義的模組:

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 CustomToastPackage(),

mImagePickerPackage,

new NativePagePackage(),

new CirclePackage(),

new UpdateAndroidPackage()

);

}

 

@Override

protected String getJSMainModuleName() {

return "index";

}

 

@Nullable

@Override

protected String getJSBundleFile() {

File file = new File(getExternalCacheDir(), FILE_NAME);

if (file != null && file.length() > 0) {

return file.getAbsolutePath();

}

return super.getJSBundleFile();

}

};

 

@Override

public ReactNativeHost getReactNativeHost() {

return mReactNativeHost;

}

5.js中通過requireNativeComponent呼叫原生UI元件.(Circle.js)

import React, { Component } from 'react';

import {

View,

requireNativeComponent,

} from 'react-native';

import {PropTypes} from 'prop-types'

 

var iFace={

name:'Circle',

propTypes:{

color:PropTypes.number,

radius:PropTypes.number,

...View.propTypes // 包含預設的View的屬性

},

nativeOnly:{

onclick:true

}

}

module.exports=requireNativeComponent('MCircle', iFace);

6.在js中對其相關處理做進一步封裝.

import React, { Component } from 'react';

import {

View,

UIManager,

findNodeHandle

} from 'react-native';

 

import {PropTypes} from 'prop-types'

import MCircle from '../native_modules/Circle'

 

var RCT_CIRCLE_REF='MCircle'

export default class Circle extends Component {

 

static propTypes = {

radius: PropTypes.number,

color: PropTypes.number, // 這裡傳過來的是string

...View.propTypes // 包含預設的View的屬性

}

onClick(event) {

if(this.props.onClick) {

if(!this.props.onClick){

return;

}

// 使用event.nativeEvent.msg獲取原生層傳遞的資料

this.props.onClick(event.nativeEvent.msg);

}

}

handleTask() {

//向native層傳送命令

// noinspection JSDeprecatedSymbols

UIManager.dispatchViewManagerCommand(

findNodeHandle(this.refs[RCT_CIRCLE_REF]),

UIManager.MCircle.Commands.handleTask,

null)

}

render() {

const { style, radius, color } = this.props;

return (

<View>

<MCircle

ref={RCT_CIRCLE_REF}

style={style}

radius={radius}

color={color}

onClick={(event)=> this.onClick(event)}

/>

</View>

);

}

}

7.直接使用:

<View style={styles.rowContainer}>

<Circle ref={(circle)=>{this.circle = circle}}

style={{width: 100, height: 100}}

color={processColor("#f45675")}

radius={50}

onClick={(msg)=>Alert.alert("js press",msg)}/>

</View>

 

最後奉上原始碼:https://github.com/RightOfHand/FirstApp.git