React Native学习之RN调用原生UI组件

React Native已经封装了大部分最常见的组件,譬如ScrollView和TextInput,但不可能封装全部组件。而且,说不定你曾经为自己以前的App还封装过一些组件,React Native肯定没法包含它们。幸运的是,在React Naitve应用程序中封装和植入已有的组件非常简单。

首先准备好原生UI组件:自定义View,第三方SDK,网络依赖,jar包

KenBurnsView

KenBurnsView组件可以显示图片的一部分,然后通过改变显示位置来显示其他部分,配上合适的图片,可以产生一种在飞机上往下看的视觉效果(俯瞰图)。

KenBurnsView:Android library that provides an extension to ImageView that creates an immersive experience by animating its drawable using the Ken Burns Effect.

GitHub:https://github.com/flavioarfaria/KenBurnsView

配置 build.gradle

dependencies {
    compile 'com.flaviofaria:kenburnsview:1.0.7'
}

点击同步执行 Gradle Build

暴露原生视图给React Native的步骤:

创建一个ViewManager的子类

实现getNamecreateViewInstance方法

  • getName 是 React Native 端能够识别的组件名称
  • createViewInstance 是组件初始化方法
public class KenBurnsViewManager extends SimpleViewManager<KenBurnsView> {
    private ThemedReactContext context;

    @Override
    public String getName() {
        return "KenBurnsView";
    }

    @Override
    protected KenBurnsView createViewInstance(ThemedReactContext reactContext) {
        return null;
    }
}

使用@ReactProp(或@ReactPropGroup)注解

要导出给JavaScript使用的属性,需要申明带有@ReactProp(或 @ReactPropGroup)注解的设置方法。方法的第一个参数是要修改属性的视图实例,第二个参数是要设置的属性值。方法的返回值类型必须为 void,而且访问控制必须被声明为 public。

//暴露属性
@ReactProp(name = "imgSource")
public void setSource(KenBurnsView view, String source){
    try {
        InputStream inputStream = context.getAssets().open(source);
        Drawable drawable = Drawable.createFromStream(inputStream, null);
        view.setImageDrawable(drawable);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

JavaScript 所得知的属性类型会由该方法第二个参数的类型来自动决定。支持的类型有:boolean, int, float, double, String, Boolean, Integer, ReadableArray, ReadableMap

@ReactProp注解必须包含一个字符串类型的参数 name。这个参数指定了对应属性在 JavaScript 端的名称。

除了 name,@ReactProp注解还接受这些可选参数:defaultBoolean, defaultInt, defaultFloat。这些参数必须是对应的基础类型的值(也就是 boolean, int, float),这些值会被传递给 setter 方法,以免 JavaScript 端某些情况下在组件中移除了对应的属性。注意这个“默认”值只对基本类型生效,对于其他的类型而言,当对应的属性删除时,null 会作为默认值提供给方法。

使用@ReactPropGroup来注解的设置方法和@ReactProp不同。请参见@ReactPropGroup注解类源代码中的文档来获取更多详情。

注意:在ReactJS中,每修改一个属性都会引发一次对设置方法的调用。有一种修改情况是,移除掉之前设置的属性,在这种情况下设置方法也一样会被调用,并且“默认”值会被作为参数提供(对于基础类型来说可以通过 defaultBoolean, defaultFloat 等 @ReactProp 的属性提供),而对于复杂类型来说参数则会设置为 null)。

创建一个ReactPackage的子类,把这个视图管理类注册到应用程序包的createViewManagers里

public class MyReactPackage 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 KenBurnsViewManager()
        );
    }
}

在MainAppcation 中的 getPackages 方法中添加 ReactPackage 的子类对象

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

实现 React Native 端的 JavaScript 模块

组件接口

import React, { Component,PropTypes } from 'react';
import {
  requireNativeComponent,
  View
} from 'react-native';

let iface={
    name:'KenBurnsView',
    propTypes:{
        imgSource:PropTypes.string,
        //imgSource:PropTypes.string.isRequired,
        ...View.propTypes
    },
};

module.exports = requireNativeComponent('KenBurnsView', iface);

requireNativeComponent通常接受两个参数,第一个参数是原生视图的名字(JAVA层KenBurnsViewManager$getName的值),而第二个参数是一个描述组件接口的对象。组件接口应当声明一个友好的 name,用于在调试信息中显示;组件接口还必须声明 propTypes 字段,用来对应到原生视图上。这个propTypes还可以用来检查用户使用 View 的方式是否正确,View.propTypes表示包含View的所有属性。最后通过module.exports导出提供给其他组件使用。

注意:如果你还需要一个 JavaScript 组件来做一些除了指定namepropTypes以外的事情,譬如事件处理,可以把原生组件使用一个普通的 React 组件封装。在这种情况下,requireNativeComponent 的第二个参数变为用于封装的组件。

注意:和原生模块不同,原生视图的前缀RCT不会被自动去掉。

组件调用

import KenBurnsView from './KenBurnsView.js';

<KenBurnsView
    imgSource='shanghai.jpg'
    style={{width:200, height:300}}
/>

源码

KenBurnsViewManager.java

public class KenBurnsViewManager extends SimpleViewManager<KenBurnsView> {
    private ThemedReactContext context;

    @Override
    public String getName() {
        return "KenBurnsView";
    }

    @Override
    protected KenBurnsView createViewInstance(ThemedReactContext reactContext) {
        context = reactContext;
        KenBurnsView kenBurnsView = new KenBurnsView(context);
        try {
            InputStream inputStream = context.getAssets().open("shanghai.jpg");
            Drawable drawable = Drawable.createFromStream(inputStream, null);
            kenBurnsView.setImageDrawable(drawable);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return kenBurnsView;
    }

    //暴露属性
    @ReactProp(name = "imgSource")
    public void setSource(KenBurnsView view, String source){
        try {
            InputStream inputStream = context.getAssets().open(source);
            Drawable drawable = Drawable.createFromStream(inputStream, null);
            view.setImageDrawable(drawable);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

MyReactPackage.java

public class MyReactPackage 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 KenBurnsViewManager()
        );
    }
}

MainApplication.java

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

KenBurnsView.js

import React, { Component,PropTypes } from 'react';
import {
  requireNativeComponent,
  View
} from 'react-native';

let iface={
    name:'KenBurnsView',
    propTypes:{
        imgSource:PropTypes.string,
        ...View.propTypes
    },
};

module.exports = requireNativeComponent('KenBurnsView', iface);

index.android.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View
} from 'react-native';

import KenBurnsView from './KenBurnsView.js';

class RNAPP extends Component {
    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.welcome}>
                    Welcome to React Native!
                </Text>
                <Text style={styles.welcome}>
                    React Native混合原生开发
                </Text>
                <Text style={styles.instructions}>
                    Powered by rnapp.cc
                </Text>
                <KenBurnsView
                    imgSource='shanghai.jpg'
                    style={{width:200, height:300}}
                    />
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
    welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
    },
    instructions: {
        textAlign: 'center',
        color: '#333333',
        marginBottom: 5,
    },
    btn: {
        marginTop: 20,
        marginLeft: 10,
        marginRight: 10,
        width: 260,
        height: 35,
        backgroundColor: '#3BC1FF',
        color: '#fff',
        lineHeight: 24,
        fontWeight: 'bold',
        textAlign: 'center',
        textAlignVertical:'center'
    },
});

AppRegistry.registerComponent('RNAPP', () => RNAPP);

版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/02/25/react-native-learning-rn-calling-native-ui-components/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
海报
React Native学习之RN调用原生UI组件
React Native已经封装了大部分最常见的组件,譬如ScrollView和TextInput,但不可能封装全部组件。而且,说不定你曾经为自己以前的App还封装过一些组件,React ……
<<上一篇
下一篇>>
文章目录
关闭
目 录