假设现在我们要画一个圆,当然,你可以用js自己去实现,但是今天要讲的并不是js去实现圆,而是在Native端去写一个View去暴露给js调用,ok,我们废话少说,进入正题
暴露一个简单控件给js调用分为以下几个步骤:
1.自定义一个View控件
2.自定义一个Manager实现SimpleViewManager
3.将自定义的Manager注册进Package
4.js调用
自定义View控件相信android老司机们早已驾轻就熟,不再啰嗦,直接看代码
public class CircleView extends View {
private static 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) {
/**
* 由于JS传过的数字是dip单位,需要转换为实际像素
* 使用com.facebook.react.uimanager包中的PixelUtil,进行转换
*/
mRadius = PixelUtil.toPixelFromDIP(radius);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);;
}
下面开始自定义Manager:
public class CircleManager extends SimpleViewManager<CircleView> {
@Override
public String getName() {
return "MCircle";
}
/**
* 创建UI组件实例
* @param reactContext
* @return CircleView
*/
@Override
protected CircleView createViewInstance(ThemedReactContext reactContext) {
return new CircleView(reactContext);
}
/**
* 传输背景色参数
* @param view
* @param color
*/
@ReactProp(name = "color")
public void setColor(CircleView view, Integer color) {
view.setColor(color);
}
/**
* 传输半径参数
* @param view
* @param radius
*/
@ReactProp(name = "radius")
public void setRadius(CircleView view, Integer radius) {
view.setRadius(radius);
}
}
注意这里的getName方法的返回值要与js里一致,这个文章后面再详细说明,先有个大致的概念,同时还需要注意的是@ReactProp这个注解
OK,我们有了Manager,现在去注册到package,很简单
public class CommPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new CommonModule(reactContext));
return modules;
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
List<ViewManager> views = new ArrayList<>();
views.add(new CircleManager());
return views;
}
}
注意到这里的createViewManagers不再返回一个emptyList,而是把CircleManager给add进去了
最后,我们写一下js代码
首先我们需要写一个Circle.js
import React, { Component } from 'react';
import { requireNativeComponent, View, processColor } from 'react-native'; //processColor=>字符Color转换为数字
import PropTypes from 'prop-types';
const MCircle = requireNativeComponent('MCircle', {
propTypes: {
color: PropTypes.number,
radius: PropTypes.number,
...View.propTypes // 包含默认的View的属性
},
});
class Circle extends Component {
static propTypes = {
radius: PropTypes.number,
color: PropTypes.string, // 这里传过来的是string
...View.propTypes // 包含默认的View的属性
}
render() {
const { style, radius, color } = this.props;
return (
<MCircle
style={style}
radius={radius}
color={processColor(color)}
/>
);
}
}
module.exports = Circle;
注意这边的requireNativeComponent有两个参数:第一个参数是MCircle,这个名字要与上面的CircleManager的getName返回值保持一致;第二个参数约定了属性的一些类型,可以看到有一个number类型的color属性,一个number类型的radius属性,这两个属性是怎么对应到Native端的,这就要说到上面的@ReactProp注解,我们截取下之前的代码
/**
* 传输背景色参数
* @param view
* @param color
*/
@ReactProp(name = "color")
public void setColor(CircleView view, Integer color) {
view.setColor(color);
}
/**
* 传输半径参数
* @param view
* @param radius
*/
@ReactProp(name = "radius")
public void setRadius(CircleView view, Integer radius) {
view.setRadius(radius);
}
可以看到@ReactProp注解有个name的key,他的value值对应的就是Circle.js里propTypes声明的属性,每当js那边设置color属性,这个setColor的方法就会自动调用,这样就把js的参数传递到了native
最后我们改下index.js
'use strict'
import React, { Component} from 'react';
import { AsyncStorage,NativeModules,ToastAndroid } from 'react-native';
import {
AppRegistry,
StyleSheet,
Text,
Image,
View
} from 'react-native';
import Circle from './Circle';
let title = 'React Native界面';
export default class YRNTest extends Component {
/**
* Callback 通信方式
*/
callbackComm(msg) {
NativeModules.CommonModule.rnCallNativeFromCallback(msg,(result) => {
ToastAndroid.show("CallBack收到消息:" + result, ToastAndroid.SHORT);
})
}
/**
* Promise 通信方式
*/
promiseComm(msg) {
NativeModules.CommonModule.rnCallNativeFromPromise(msg).then(
(result) =>{
ToastAndroid.show("Promise收到消息:" + result, ToastAndroid.SHORT)
}
).catch((error) =>{console.log(error)});
}
render() {
return (
<View style={styles.container}>
<Circle
style={{width: 100, height: 100}}
color="#25c5f7"
radius={50}
/>
</View>
// <View style={styles.container}>
// <Text style={styles.welcome} >
// {title}
// </Text>
// <Text style={styles.welcome} onPress={this.callbackComm.bind(this,'你好啊,android')}>
// Callback通信方式
// </Text>
// <Text style={styles.welcome} onPress={this.promiseComm.bind(this,'你好啊,android')}>
// Promise通信方式
// </Text>
// </View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#FFFFFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
}
});
AppRegistry.registerComponent('YRNTest', () => YRNTest);
添加import Circle from './Circle';
render里改为
<View style={styles.container}>
<Circle
style={{width: 100, height: 100}}
color="#25c5f7"
radius={50}
/>
</View>
运行工程,大功告成,附上截图