上一篇文章[React Native]原生UI组件(上)我们介绍了如何在ReactNative
中使用原生UI组件,并与原生UI组件进行“数据”交互。
但是,更多情况下,我们不会仅局限于“数据”的交互,我们更想知道在原生UI组件上用户触发的“事件”,并将这些事件“反馈”到ReactNative
。这样,才能在ReactNative
中去完成我们的业务逻辑。
上一篇文章的示例:“仿QQ”滑动删除消息组件SwipeMenuListView
中,我们使用setMenuCreator
方法为SwipeMenuListView
的Item
设置菜单,也就是下图我们所看到的,当你向左滑动某一行的数据,会出现一个“删除”的菜单图标。
那么,为了更好的演示本篇文章的主题:“事件”交互,我们会在SwipeMenuListView
示例的基础之上,一步步实现如何在点击“删除”菜单的时候,把列表中的数据删除,并且把这条删除的数据的“反馈”到ReactNative
中。同时在ReactNative
使用ToastAndroid
组件,“显示”用户刚刚删除的数据。
如果要处理原生UI组件的事件,我们需要将原生UI组件用一个普通的React组件进行封装。
第一步:新建一个SwipeMenuListViewComponent.js
文件,定义一个组件SwipeMenuListViewComponent
class SwipeMenuListViewComponent extends Component {
}
第二步:引入原生导出的SwipeMenuListView
,并将SwipeMenuListView
与React
组件SwipeMenuListViewComponent
进行关联
// 第一个参数是原生模块的名称, 第二个是当前组件的名称
var SwipeMenuListView = requireNativeComponent('SwipeMenuListView', SwipeMenuListViewComponent, {
nativeOnly: {onChange: true}
});
这里,第三个参数nativeOnly
有些特殊。这个参数的作用在于,如果你想导出某些原生UI的属性,但你又不希望它成为React封装组件的属性,所以你不能放在propTypes
中。可是如果你不放的话,又会产生一个错误,解决的办法就是带上nativeOnly
参数
第三步:使用AndroidStudio打开android项目,修改AppViewManager.java文件的createViewInstance
方法
protected SwipeMenuListView createViewInstance(final ThemedReactContext reactContext) {
// ...省略
// bind menu click listener
swipeMenuListView.setOnMenuItemClickListener(new SwipeMenuListView.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(int position, SwipeMenu menu, int index) {
switch (index)
{
case 0: // delete menu
// 删除数据
String language = adapter.getItem(position);
dataSource.remove(position);
adapter.notifyDataSetChanged();
WritableMap map = Arguments.createMap();
map.putString("language", language);
// "topChange"事件在JS端映射到"onChange",参考UIManagerModuleConstants.java
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(swipeMenuListView.getId()
, "topChange", map);
break;
}
return false;
}
});
// ...省略
}
点击“删除”菜单,首先使用dataSource.remove(position);
删除一行数据,然后通知Adapter
刷新adapter.notifyDataSetChanged();
,我们来看下这个方法
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(swipeMenuListView.getId(), "topChange", map);
这个方法主要作用就是向ReactNative
发送给topChange
事件,并将参数使用WritableMap
携带给ReactNative
,这里的topChange
被映射到JS的onChange
。这样,在 React 层就能这样用了:<SwipeMenuListView onChange={xxx}/>
topChange
事件在JS端映射到onChange
,这个过程是由ReactNative
帮我们完成的,参考UIManagerModuleConstants.java
第四步:实现SwipeMenuListViewComponent
的render
方法
// ...省略
render() {
// onChange事件是JS已经定义好的,对应原生的topChange事件
return <SwipeMenuListView {...this.props} onChange={(event)=>{
this.props.onDelete(event);
}}/>;
}
// ...省略
onChange
对应原生UI组件发送的topChange
事件。这里,我们将事件对应到我们在propTypes
中声明的func:onDelete
,这样是为了外部引用SwipeMenuListViewComponent
组件的地方可以使用这个事件
SwipeMenuListViewComponent.propTypes = {
array: PropTypes.arrayOf(PropTypes.string),
onDelete: PropTypes.func,
...View.propTypes, // 包含默认的View的属性
};
第五步:也是最后一步,在index.android.js
中使用SwipeMenuListViewComponent
组件
// ... 省略
var SwipeMenuListView = require('./SwipeMenuListViewComponent');
class Demo4 extends Component {
render() {
return (
<View style={styles.container}>
<SwipeMenuListView style={styles.listView} array={["Java", "C", "C++", "C#", "Python", "PHP"
, "Visual Basic .NET", "JavaScript", "Assembly Language", "Ruby", "Perl"
, "Delphi", "Visual Basic", "Swift", "MATLAB", "Pascal"]}
onDelete={(event)=>{
ToastAndroid.show(event.nativeEvent.language, ToastAndroid.SHORT);
}
}>
</SwipeMenuListView>
</View>
);
}
}
// ... 省略
好了,关于原生UI组件的讲解就到此结束了。如有不正确之处,欢迎指正~
本文的源码地址:Demo4
网友评论