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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a992036795/article/details/73540335

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

猜你喜欢

转载自blog.csdn.net/a992036795/article/details/73540335