ReactNative开发——封装原生UI组件

来源:互联网 发布:.net程序员面试指南 编辑:程序博客网 时间:2024/06/03 14:26

ReactNative开发——封装原生UI组件

下文我们将制作一个可以用来显示图片的原生UI组件,这个UI组件可以随着手势放大缩小。(封装PhotoView)
PhotoView的开源地址:https://github.com/chrisbanes/PhotoView

一、引入开源库

可以PhotoView开源库作者的引用提示:
1、在 android项目根目录中的 build.gradle中加入:

allprojects {...maven { url "https://jitpack.io" }}

2、在android Moudle中的build.gralde 中加入

dependencies {  ....    compile 'com.github.chrisbanes:PhotoView:2.0.0'    compile 'com.github.bumptech.glide:glide:4.0.0-RC0'    annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0-RC0'}

额,还要引用glide方便我们加载网络图片。

二、在Native端建立一个ViewManager。

1、我们可以直接创建一个类继承com.facebook.react.uimanager.SimpleViewManager 然后重新它的 getName,createViewInstance方法

public class PhotoViewManger extends SimpleViewManager<PhotoView> {    private static final String TAG = "PhotoViewManger";    private static final String REACT_CLASS = "PhotoView";    private ThemedReactContext aContext;    private PhotoView photoView;    @Override    public String getName() {        return REACT_CLASS;    }    @Override    protected PhotoView createViewInstance(ThemedReactContext reactContext) {        this.aContext = reactContext;        photoView = new PhotoView(reactContext);        photoView.setImageResource(R.mipmap.ic_launcher);        });        return photoView;    }}

三、通过@ReactProp(或 @ReactPropGroup)注解来导出属性

例如:

@ReactProp(name = "imgSource")public void setSource(PhotoView photoView, String source) {    Glide.with(aContext).load(source).into(photoView);}

这里声明了一个属性,属性名为imgSource。注意:这个方法必须是public的,其中第一个时我们createViewInstance返回的那个View,第二个参数使我们接受的参数类型,可以为boolean int float double String Boolean Integer com.facebook.react.bridge.ReadableArray com.facebook.react.bridge.ReadableMap

注解中也可以设置默认值defaultInt defaultDouble 等等。

四、创建一个ReactPackage的实现类

创建一类继承自ReactPackage,并在createViewManagers返回List中加入我们定义的ViewManager。

public class PhotoViewPackage 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 PhotoViewManger());    }}

五、在ReactNativeHost中加入我们定义的ReactPackage实现类

private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {    @Override    public boolean getUseDeveloperSupport() {        return BuildConfig.DEBUG;    }    @Override    protected List<ReactPackage> getPackages() {        return Arrays.<ReactPackage>asList(                new MainReactPackage(),                new PhotoViewPackage()        );    }};

六、在React Native端使用

import React from 'react';import {requireNativeComponent, View} from 'react-native';let iface = {    name: 'PhotoView',    propTypes: {        imgSource: React.PropTypes.string,        ...View.propTypes  //支持View组件的所有属性    }}var RCTPhotoView = requireNativeComponent('PhotoView', iface);export default RCTPhotoView;

核心方法:requireNativeComponent('PhotoView', iface)
这个方法可以接收三个参数:
第一个参数:stirng 类型,对应我们Native中定义的那个ViewManager#getName的返回值。
第二个参数:{name?:string,displayName?:string,propTypes:Object}类型,或者ReactClass类型
第三个参数:{nativeOnly?:Object} 类型,用来解决 一些特殊的属性,想从原生组件中导出,但是又不希望它们成为对应React封装组件的属性

封装好之后,我们就可以想使用ReactNative组件那样使用了

class Main extends Component {    render() {        return (            <View style={{flex: 1}}>                <PhotoView                    onChange={(obj) => {                        Alert.alert('scale',obj.nativeEvent.msg);                    }}                    style={{flex: 1, width: '100%'}}                    imgSource='https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1497959956050&di=d9ac82e7c9ba4fde2836510aeb2f2248&imgtype=0&src=http%3A%2F%2Fimg.tupianzj.com%2Fuploads%2Fallimg%2F160309%2F9-16030Z92137.jpg'                />            </View>        );    }}AppRegistry.registerComponent('HybridUsage', () => Main);

七、事件

下面我们将演示怎么将Native UI 组件的事件导出。

我以导出PhotoViewOnScaleChangeListener为例

1、给native发送事件

   photoView.setOnScaleChangeListener(new OnScaleChangedListener() {            @Override            public void onScaleChange(float scaleFactor, float focusX, float focusY) {                WritableMap map = Arguments.createMap();                map.putInt("target", photoView.getId());                map.putString("msg", "ScaleInfo: scaleFactor:" + scaleFactor                        + ",focusX" + focusX + ",focusY" + focusY);                /**                 * {@link com.facebook.react.uimanager.UIManagerModuleConstants}                 */                aContext.getJSModule(RCTEventEmitter.class).receiveEvent(                        photoView.getId(), "topChange", map                );            }        });

核心方法:

aContext.getJSModule(RCTEventEmitter.class).receiveEvent(        photoView.getId(), "topChange", map);

这里第一个参数NavieUi组件的getId,第二个参数为事件的名称 我们传入”topChange”实际对应的的ReactNative端的”onChange”函数
我们可以查看com.facebook.react.uimanager.UIManagerModuleConstants查看对应关系。

2、ReactNative端

我们给属性多设置一个onChange方法,用来接受Native UI组件的回调

let iface = {    name: 'PhotoView',    propTypes: {        imgSource: React.PropTypes.string,        //回调        onChange: React.PropTypes.func,        ...View.propTypes  //支持View组件的所有属性    }}var RCTPhotoView = requireNativeComponent('PhotoView', iface);export default RCTPhotoView;

3、使用

/** * Created by blueberry on 6/20/2017. */import React, {Component} from 'react';import {AppRegistry, StyleSheet, View, Text, requireNativeComponent, TextInput, Button, Alert} from 'react-native';import PhotoView from './PhotoView';class Main extends Component {    render() {        return (            <View style={{flex: 1}}>                <PhotoView                    onChange={(obj) => {                        Alert.alert('scale',obj.nativeEvent.msg);                    }}                    style={{flex: 1, width: '100%'}}                    imgSource='https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1497959956050&di=d9ac82e7c9ba4fde2836510aeb2f2248&imgtype=0&src=http%3A%2F%2Fimg.tupianzj.com%2Fuploads%2Fallimg%2F160309%2F9-16030Z92137.jpg'                />            </View>        );    }}AppRegistry.registerComponent('HybridUsage', () => Main);

另外Native端还有一种方法,也可以导出事件

对应的java代码:

 photoView.setOnScaleChangeListener(new OnScaleChangedListener() {            @Override            public void onScaleChange(float scaleFactor, float focusX, float focusY) {                // 这里又2中方案 :  1.                aContext.getNativeModule(UIManagerModule.class).getEventDispatcher()                        .dispatchEvent(new ReactScaleChangeEvent(photoView.getId(),                                "ScaleInfo: scaleFactor:" + scaleFactor                                        + ",focusX" + focusX + ",focusY" + focusY));//                // 2.//                WritableMap map = Arguments.createMap();//                map.putInt("target", photoView.getId());//                map.putString("msg", "ScaleInfo: scaleFactor:" + scaleFactor//                        + ",focusX" + focusX + ",focusY" + focusY);////                /**//                 * {@link com.facebook.react.uimanager.UIManagerModuleConstants}//                 *///                aContext.getJSModule(RCTEventEmitter.class).receiveEvent(//                        photoView.getId(), "topChange", map//                );            }        });/** * {@link com.facebook.react.uimanager.UIManagerModuleConstants} */class ReactScaleChangeEvent extends Event<ReactScaleChangeEvent> {    public static final String EVENT_NAME = "topChange"; //会被映射为onChange 具体映射关系参见 UIManagerModuleConstants.java    private String msg;    ReactScaleChangeEvent(int viewId, String msg) {        super(viewId);        this.msg = msg;    }    @Override    public String getEventName() {        return EVENT_NAME;    }    @Override    public void dispatch(RCTEventEmitter rctEventEmitter) {        rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());    }    private WritableMap serializeEventData() {        WritableMap map = Arguments.createMap();        map.putInt("target", getViewTag());        map.putString("msg", this.msg);        return map;    }}

示例代码地址:

https://github.com/blueberryCoder/RNDemo/blob/master/HybridUsage/android/app/src/main/java/com/hybridusage/PhotoViewManger.java

原创粉丝点击