[React Native]原生UI组件(上)

作者: zhuhf | 来源:发表于2016-06-06 22:07 被阅读2523次

    前面两篇文章主要介绍了如何在ReactNative中集成并使用原生模块的代码,本篇文章会讲解另一个和原生模块有关的重点内容,那就是在ReactNative中使用已有的原生UI组件。

    总所周知,移动App快速发展的这几年,用户对于交互体验(UI、UE)的要求越来越高,因此,在原生开发中涌现了很多优秀的UI组件。ReactNative做为一个新的技术方向,目前在UI组件的积累难免还没有原生那么丰富和强大,所以我们必须要学会如何在ReactNative中使用一些我们已有的原生UI组件,并能和这些组件进行“交流”(数据、事件等)。

    本文会演示如何在ReactNative中使用“仿QQ”滑动删除消息组件SwipeMenuListView,对这个控件不熟悉的朋友请先查看官方说明,本篇文章的最终效果图如下

    device-2016-06-05-204355_0-76.gif

    正如上图所演示的,使用原生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模块就可以传递数据给原生视图(或变量、属性等)来改变原生视图的样式和行为。

    提供原生视图的步骤如下:

    1. 创建一个SimpleViewManager的子类AppViewManager
    2. 实现createViewInstance方法
    3. 导出视图的属性:使用@ReactProp注解来导出属性的设置方法
    4. 注册视图
    5. 实现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,然后使用MyAdapterSwipeMenuListView绑定一个数据源。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需要在MainActivitygetPackages中注册并返回。

    ...
    @Override
    protected List<ReactPackage> getPackages() {
        return Arrays.<ReactPackage>asList(
            new MainReactPackage(),
            new AppReactPackage()
        );
    }
    

    5. 实现JS模块
    我们需要新建一个JS文件,来描述我们在AppViewManagergetName方法返回的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通常接受两个参数:
    第一个参数AppViewManagergetName方法返回的视图名称。
    第二个参数是一个描述UI组件接口的对象,这个对象通常包括两个重要的部分:

    1. name:便于调试时显示(你可以设置为任意字符串)。
    1. 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组件(下)

    相关文章

      网友评论

      • shawbs:新手看不懂,哪怕github上的demo代码也跑不起来。自己本地按照修改代码,哪些包该引入,哪些细节要注意,一概不知。不知楼主能不能写个完全适合新手的关于原生UI组件使用的文章?(官网和百度我都有查过,没找到满意的)
      • putaozhuose:楼主真大神,谢谢楼主
      • 08e7aa3755a2:mark,谢谢楼主的分享!说得很详细很适合我这样的新手!

      本文标题:[React Native]原生UI组件(上)

      本文链接:https://www.haomeiwen.com/subject/xarldttx.html