参考文档官方版:Native UI Components
上面的文档介绍了facebook 开发小组,如何封装原生组件ImageView给js调用,但是没有具体的实例。本文以封装原生TextView为例,一步一步的实现一个简单示例。
提供原生视图很简单:
- 创建一个ViewManager的子类(或者更常见的,
SimpleViewManage
的派生类)。 - 实现
createViewInstance
方法。 - 导出视图的属性设置器:使用
@ReactProp
(或@ReactPropGroup
)注解。 - 把这个视图管理类注册到应用程序包的
createViewManagers
里。 - 实现JavaScript模块。
react-native init NativeView
第一步. 创建ViewManager
的子类MyTextViewManager
第二步.实现方法createViewInstance
第三步. 通过@ReactPropGroup
)注解来导出属性的设置方法
上面三步中MyTextViewManager的整个代码如下:
public class MyTextViewManager extends SimpleViewManager<TextView> { @Override public String getName() { return "MyTextView"; } @Override protected TextView createViewInstance(ThemedReactContext reactContext) { TextView textView = new TextView(reactContext); return textView; } @ReactProp(name = "text") public void setText(TextView view,String text) { view.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); }
@ReactProp(name = @H_502_185@"isAlpha",defaultBoolean = false)public void setTextAlpha(TextView view,boolean isAlpha) { if (isAlpha) { view.setAlpha(0.5f);} }}
第四步:注册MyTextViewManager。
创建类MyReactPackage,实现ReactPackage的方法createViewManager,在该方法中注册上面的组件MyTextViewManager。实现ReactPackage时,需要实现这三个方法,学过导入原生模块部分时我们应该很熟悉了。封装的原生模块放在createNativeModules里,封装的原生UI组件放在createViewManagers里。需要注意的是剩下的最后一个方法createJSModules里默认是返回null,要改成返回空集合,否则编译时会报错。新建一个MyTextView.js文件。代码如下:propTypes代码如下:public class MyReactPackage implements ReactPackage { @Override public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { return Collections.emptyList(); } @Override public List<Class<? extends JavaScriptModule>> createJSModules() { return Collections.emptyList(); } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Arrays.<ViewManager>asList( new MyTextViewManager() ); } }MyReactPackage还需要在MainApplication.java文件的getPackages方法中提供。这个文件位于你的react-native应用文件夹的android目录中。具体路径是: android/app/src/main/java/com/your-app-name/MainApplication.java.@Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(),new MyReactPackage() ); }第五步:实现对应的JS模块。
import {requireNativeComponent,View} from'react-native';
var myTextView ={
name:'MyTextView',
propTypes:{
text:PropTypes.string,
textSize:PropTypes.number,
textColor:PropTypes.number,
isAlpha:PropTypes.bool,
...View.propTypes // 包含默认的View的属性
}
}
module.exports =requireNativeComponent('MyTextView',myTextView);
最后:然后你就可以在js代码中引用刚才的组件了,引用例子:
import React,{ Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
var MyTextView = require('./MyTextView');
class NativeView extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.outView}>
<MyTextView
style={styles.myTextView}
text={"Welcome you Andy!"}
textSize={25}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
justifyContent:'center',
alignItems:'center',
flex: 1,
backgroundColor: '#F5FCFF',
},
outView:{
borderWidth:2,
},
myTextView:{
width:300,
height:50,
});
AppRegistry.registerComponent('NativeView',() => NativeView);
事件
现在我们已经知道了怎么导出一个原生视图组件,并且我们可以在JS里很方便的控制它了。不过我们怎么才能处理来自用户的事件,譬如用户的点击?当一个原生事件发生的时候,它应该也能触发JavaScript端视图上的事件,这两个视图会依据getId()而关联在一起。
在MyTextViewManager中,修改createViewInstance方法。代码如下:
@Override protected TextView createViewInstance(final ThemedReactContext reactContext) { final TextView textView = new TextView(reactContext); textView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v,MotionEvent event) { if(event.getAction()== MotionEvent.ACTION_DOWN){ WritableMap nativeEvent= Arguments.createMap(); nativeEvent.putString("message","MyMessage"); reactContext.getJSModule(RCTEventEmitter.class).receiveEvent( textView.getId(),"topChange",nativeEvent ); return true; }else{ return false; } } }); return textView; }这是在创建的view实例中,当发生本地事件时进行事件注册。这个事件名topChange 在JavaScript端映射到onChange
回调属性上,这个映射关系是固定的被官方写在UIManagerModuleConstants.java 文件里了。这个回调会被原生事件执行,
然后我们通常会封装组建里构造一个类似的API,修改上面的MyTextView.js文件,完整代码如下
import { PropTypes } from 'react';
import {requireNativeComponent,View} from'react-native';
var myTextView ={
name:'MyTextView',
propTypes:{
text:PropTypes.string,
...View.propTypes // 包含默认的View的属性
}
}
//module.exports =requireNativeComponent('MyTextView',myTextView);
var RCTMyView=requireNativeComponent('MyTextView',myTextView);
import React,{ Component } from 'react';
class MyView extends Component{
constructor(){
super();
this._onChange = this._onChange.bind(this);
}
_onChange(event:Event){
if(!this.props.onChangeMessage){
return;
}
if(event.nativeEvent.message === 'MyMessage'){
this.props.onChangeMessage();
return;
}
}
render(){
return<RCTMyView
{...this.props}
onChange = {this._onChange}/>
}
}
MyView.propTypes = {
onChangeMessage:React.PropTypes.func,
}
module.exports = MyView;
这里用MyView对myTextView进行了一次封装。注意到在MyView里为onChange绑定了_onChange方法,在这个方法里我们会调用一个预定义为函数的onChangeMessage。而之前已经在native端将topChange绑定了原生的onTouch事件,topChange又会映射到JS端的onChange属性,这样最后当原生的onTouch事件发生时,就会调用JS端定义的onChangeMessage函数,就实现了两端事件的互动。其实onTouch事件对应到onChange属性后就已经实现了事件绑定,写onChangeMessage是为了示例展示而已。
现在只需在index.android.js文件中添加几行代码就可以看到效果了
只需为上面的index.android.js文件添加几行代码即可。
_onButtonPress(){
alert("haha,you pressed me");
this.setState({
text:"bind event successful!"
});
}
再给MyTextView添加一个属性onChangeMessage={()=>this._onButtonPress()}
编译并运行,就可以看到下面的效果