[React Native]原生UI组件(上)
前面两篇文章主要介绍了如何在ReactNative中集成并使用原生模块的代码,本篇文章会讲解另一个和原生模块有关的重点内容,那就是在ReactNative中使用已有的原生UI组件。
总所周知,移动App快速发展的这几年,用户对于交互体验(UI、UE)的要求越来越高,因此,在原生开发中涌现了很多优秀的UI组件。ReactNative做为一个新的技术方向,目前在UI组件的积累难免还没有原生那么丰富和强大,所以我们必须要学会如何在ReactNative中使用一些我们已有的原生UI组件,并能和这些组件进行“交流”(数据、事件等)。
本文会演示如何在ReactNative中使用“仿QQ”滑动删除消息组件SwipeMenuListView
,对这个控件不熟悉的朋友请先查看官方说明,本篇文章的最终效果图如下
正如上图所演示的,使用原生UI会涉及到"数据"和"事件"两个部分,由于篇幅较长,我们会分成上下两篇文章讲解,本篇文章会讲解"数据"部分。
那么,下面我们将一步步演示如何实现上面这个图的效果
- 步骤一:新建ReactNative项目
使用下面的命令新建一个ReactNative项目
react-native init Demo4
让我们运行下新建的项目,效果如下
Paste_Image.png
- 步骤二:在原生项目中引入
SwipeMenuListView
使用AndroidStudio打开Demo4目录下的android文件夹,打开app
目录下的build.gradle文件,在dependencies
节点中加入以下部分
compile 'com.baoyz.swipemenulistview:library:1.3.0'
- 步骤三:导出原生视图
SwipeMenuListView
给JS模块使用
原生视图需要被ViewManager
创建和管理,这样才能被JS模块使用。ViewManager
是一个抽象的类,我们可以使用它的派生类SimpleViewManager
来实现,另外SimpleViewManager
还可以将我们的原生视图的属性导出给JS模块,这样我们在JS模块就可以传递数据给原生视图(或变量、属性等)来改变原生视图的样式和行为。
提供原生视图的步骤如下:
- 创建一个
SimpleViewManager
的子类AppViewManager
- 实现
createViewInstance
方法 - 导出视图的属性:使用
@ReactProp
注解来导出属性的设置方法 - 注册视图
- 实现JS模块
第4步与我们前面两篇文章讲解的的原生模块比较一致,原生模块和UI都需要注册才能使用,下面我们将详细介绍这5个步骤。
1. 创建一个SimpleViewManager
的子类
...
public class AppViewManager extends SimpleViewManager<SwipeMenuListView> {
@Override
public String getName() {
return "SwipeMenuListView";
}
这个例子里我们创建一个视图管理类AppViewManager
,它继承自SimpleViewManager<SwipeMenuListView>
,SwipeMenuListView
是这个视图管理类所管理的对象类型,这应当是一个自定义的原生视图,getName
方法会用于JS端引用这个原生视图。
2. 实现createViewInstance
方法
...
@Override
protected SwipeMenuListView createViewInstance(final ThemedReactContext reactContext) {
SwipeMenuListView swipeMenuListView = new SwipeMenuListView(reactContext);
// set creator
swipeMenuListView.setMenuCreator(initMenu(reactContext));
return swipeMenuListView;
}
这里,我们直接new
一个SwipeMenuListView
,然后使用setMenuCreator
方法设置菜单,我们写了一个initMenu
方法来封装菜单的实现过程,由于这里不是本文介绍的重点,这里不做分析,有兴趣的朋友可以查看文章最后的源代码。
3. 导出视图的属性:使用@ReactProp
注解来导出属性的设置方法
这里我们引用官方的说明:
要导出属性给
JS
使用,需要申明带有@ReactProp注解的设置方法。方法的第一个参数是要修改属性的视图实例,第二个参数是要设置的属性值。方法的返回值类型必须为void,而且访问控制必须被声明为public。JavaScript所得知的属性类型会由该方法第二个参数的类型来自动决定。支持的类型有:boolean, int, float, double, String, Boolean, Integer,ReadableArray, ReadableMap。
...
/**
* 导出属性"array"给JS模块调用
* @param swipeMenuListView
* @param array
*/
@ReactProp(name = "array")
public void setDataSource(SwipeMenuListView swipeMenuListView, ReadableArray array)
{
dataSource = new ArrayList<>();
for(int i = 0; i < array.size(); i++)
{
dataSource.add(array.getString(i));
}
adapter = new MyAdapter(mContext, dataSource);
swipeMenuListView.setAdapter(adapter);
}
这个方法做的事情很简单,主要是将ReadableArray
类型的数据转换为我们熟知的ArrayList
,然后使用MyAdapter
为SwipeMenuListView
绑定一个数据源。MyAdapter也很简单,它的布局仅有一个TextView
,这里不做分析。
4. 注册视图
在Java中的最后一步就是将视图控制器注册到应用中,和[React Native]原生模块(上)介绍的一样,我们需要新建一个class:ReactPackager
,并且实现ReactPackage
接口。不同的是,原生模块需要添加到createNativeModules
中,而原生UI则需要添加到createViewManagers
中。
...
public class AppReactPackage 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) {
// 注册AppViewManager
List<ViewManager> viewManagers = new ArrayList<>();
viewManagers.add(new AppViewManager());
return viewManagers;
}
}
当然,AppReactPackage
需要在MainActivity
的getPackages
中注册并返回。
...
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new AppReactPackage()
);
}
5. 实现JS模块
我们需要新建一个JS
文件,来描述我们在AppViewManager
中getName
方法返回的SwipeMenuListView
'use strict';
var {
PropTypes
} = require('react');
var {
requireNativeComponent, View
} = require('react-native');
var iface = {
name: 'SwipeMenuListView',
propTypes: {
array: PropTypes.arrayOf(PropTypes.string),
...View.propTypes,// 包含默认的View的属性
},
};
module.exports = requireNativeComponent('SwipeMenuListView', iface);
requireNativeComponent
通常接受两个参数:
第一个参数是AppViewManager
中getName
方法返回的视图名称。
第二个参数是一个描述UI组件接口的对象,这个对象通常包括两个重要的部分:
- name:便于调试时显示(你可以设置为任意字符串)。
-
propTypes:用来声明
@ReactProp(name = "array")
注解中array
参数的JS
类型,这里PropTypes.arrayOf(PropTypes.string)
表示字符串类型的数组。另外...View.propTypes
用来声明View的默认属性,记住这一个声明是必须的,否则你将无法正确使用原生UI。
最后,我们看下如何在JS模块使用这个原生UI
...
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"]}>
</SwipeMenuListView>
</View>
);
}
}
...
AppRegistry.registerComponent('Demo4', () => Demo4);
这里需要说明的是,一定要指明SwipeMenuListView
的高度和宽度,否则无法显示。
本文的源码地址:Demo4
下一篇文章会介绍ReactNative与原生UI组件“事件”交互的内容,感兴趣的朋友请继续阅读[React Native]原生UI组件(下)。