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