React Native 自定义Android原生组件

华宏逸
2023-12-01

封装自定义Rn组件

未搭建0.58.6开发环境的小伙伴阔以查看上篇文章:Android原生集成RN

组件目录结构

── android   // 存放Android library
│   ├── build
│   ├── build.gradle
│   ├── libs
│   ├── proguard-rules.pro
│   ├── src
├── js // 存放js桥接过来的组件以及api
│   └── JMVideoLineView.js
├── index.js
├── ios // 存放iOS library
│   ├── TestModule
│   └── TestModule.xcodeproj
└── package.json // node包的配置文件

封装IOS(此处没实现在Android收集手机也是可以)

封装Android

1、创建Android Library

用Android studio打开RN工程中的Android工程,新建file->new->new module…->Android library,并命名为react-native-videolineview-jm

2、配置依赖

在app工程中的build.gradle文件中的dependencies添加一行

implementation project(':react-native-videolineview-jm')

让主工程app依赖我们新创建的Library。然后需要在我们新创建的react-native-videolineview-jm下的build.gradle中的dependencies添加一行

implementation "com.facebook.react:react-native:+"

就行了。

3、编写原生代码

  • 创建ViewManager的子类
public class JMVideoLineViewManager extends SimpleViewManager<VideoLineView> {

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

    @Override
    protected VideoLineView createViewInstance(ThemedReactContext reactContext) {
        return new VideoLineView((Context)(reactContext));
    }

}

它继承自SimpleViewManager。ReactImageView是这个视图管理类所管理的对象类型,也就是我们自定义的原生视图。getName方法返回的名字会用于在 JavaScript 端引用

  • Register the ViewManager
public class JMVideoLineViewPackage implements ReactPackage {


    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        return Arrays.asList(
                new JMVideoLineViewModule(reactContext)
        );
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.asList(
                new JMVideoLineViewManager()
        );
    }
}

原生代码写完把新创建的Library所有文件复制到node_modules/android 下,并在setting/gradle 配置

project(':react-native-videolineview-jm').projectDir = new File(rootProject.projectDir, './node_modules/react-native-videolineview-jm/android')

React组件创建

1、接下来你需要一些Javascript代码(JMVideoLineView.js,放在js文件夹)来让这个视图变成一个可用的React组件:

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {
    requireNativeComponent, View, UIManager,
    findNodeHandle,
} from 'react-native';


var RCT_VIDEO_REF = 'JMVideoLineView';

class RNVideoLineView extends Component {

    constructor(props) {
        super(props);
    }


    render() {
        return <JMF1VideoLineView
            {...this.props}
        />;
    };
}

VideoView.propTypes = {
    ...View.propTypes,
};

var JMVideoLineView = requireNativeComponent('JMVideoLineView', RNVideoLineView);

module.exports = RNVideoLineView;

创建 JavaScript 模块并且定义 Java 和 JavaScript 之间的接口层。我们建议你使用 Flow 或是 TypeScript 来规范定义接口的具体结构,或者至少用注释说明清楚(老版本的 RN 使用propTypes来规范接口定义,这一做法已不再支持)

2、在路径/node_modules/react-native-videolineview-jm创建index.js文件,导出JMVideoLineView组件

import _JMVideoLineView from './js/JMVideoLineView';

export const JMVideoLineView= _JMVideoLineView;

事件监听(Android)

native端

native端可以使用RCTEventEmitter将事件传递到JS端。基本的用法为

reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(yourView.getId(), "topChange", event);

receiveEvent第一个参数是viewId,第二个参数是eventName,topChange对应JS接收属性为onChange,第三个参数是需要传递的event。
比如我们可以将JMVideoLineView滑动进度传递到JS端,并携带一个参数,如:

//VideoLineView.java
WritableMap eventMap = Arguments.createMap();
        eventMap.putDouble("progress", time);
        ReactContext reactContext = (ReactContext) getContext();
        reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
                getId(),
 //                "onProgress",
               "topChange",
                eventMap
        );

JS端

//JMVideoLineView.js

class RNVideoLineView extends Component {
    _onChange(event) {
        alert(event.nativeEvent.progress)//这里获取到传过来的值,就是对应的key

    }


    constructor(props) {
        super(props);
    }


    render() {
        return <JMVideoLineView
            {...this.props}
            onChange={this._onChange.bind(this)}
        />;
    };
}

RNVideoLineView.propTypes = {

    ...View.propTypes,

};

var JMVideoLineView = requireNativeComponent('JMVideoLineView', RNVideoLineView,{nativeOnly:{onChange:true}});

module.exports = RNVideoLineView;

3 事件名称和JS端props对应关系

为什么事件名称topChange对应JS端onChange属性呢,好像也没有定义这个对应关系啊?其实在ViewManager中预先定义好了一些对应关系在UIManagerModuleConstants.java中:

//UIManagerModuleConstants.java
/* package */ static Map getBubblingEventTypeConstants() {
    return MapBuilder.builder()
        .put(
            "topChange",
            MapBuilder.of(
                "phasedRegistrationNames",
                MapBuilder.of("bubbled", "onChange", "captured", "onChangeCapture")))
        .put(
            "topSelect",
            MapBuilder.of(
                "phasedRegistrationNames",
                MapBuilder.of("bubbled", "onSelect", "captured", "onSelectCapture")))
        ...
        .build();
  }

那如果我们想自己定义对应关系,该怎么做呢,其实很简单,只需要复写ViewManager中getExportedCustomDirectEventTypeConstants()方法就行了。

    @Nullable
    @Override
    public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
        return MapBuilder.<String, Object>builder()
        .put("onProgress", 
        MapBuilder.of("registrationName", "onProgress"))
        .build();
    }

这样就把native和js关联起来了。

ReactProp 和 ReactMethod使用

1、ReactProp

要导出给 JavaScript 使用的属性,需要申明带有@ReactProp(或@ReactPropGroup)注解的设置方法。方法的第一个参数是要修改属性的视图实例,第二个参数是要设置的属性值。方法的返回值类型必须为void,而且访问控制必须被声明为public。JavaScript 所得知的属性类型会由该方法第二个参数的类型来自动决定。支持的类型有:boolean, int, float, double, String, Boolean, Integer, ReadableArray, ReadableMap。

//JMVideoLineViewManager.java
 @ReactProp(name = "lineColor")
    public void setLineColor(VideoLineView videoLineView,String lineColor)  {
        videoLineView.setLineColor(lineColor);
    }

    @ReactProp(name = "centerLineColor")
    public void setCenterLineColor(VideoLineView videoLineView,String centerLineColor)  {
        videoLineView.setmCenterlineColor(centerLineColor);
    }
//js中使用
 <JMVideoLineView
        style={{height: 40, backgroundColor: '#000000'}}
        lineColor={'#A7B1C5'}
        centerLineColor={'#8690A8'}
        onProgress={this._onProgress}
        />

2、ReactMethod使用

//JMVideoLineViewModule.java
@ReactMethod
    public void startAutoRun(boolean isAutoRun){

    }

在js中 JMVideoLineView.startAutoRun(true)

组件使用

在需要的页面import { JMVideoLineView } from ‘react-native-videolineview-jm’

<JMVideoLineView
        style={{height: 40, backgroundColor: '#000000'}}
        lineColor={'#A7B1C5'}
        centerLineColor={'#8690A8'}
        onProgress={this._onProgress}  //这个是自定义监听
        />

发布组件

创建github 工程

创建名为react-native-videolineview-jm 的空工程,拉取到本地

创建package.json

cd 到node_module/react-native-videolineview-jm 输入npm init
输入信息后完成即可

推送到github

复制node_module下的react-native-videolineview-jm 到上一步创建的空工程,记住复制时要保留空工程的.gitinore 文件,然后推到github上

推到github遇到一个坑

Logon failed, use ctrl+c to cancel basic credential prompt.
remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead.
remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information.
fatal: Authentication failed for 'https://github.com/JimiPlatform/react-native-videolineview-jm.git/'

原因是August 13, 2021后 github 不支持密码验证操作仓库

解决方案参照:https://blog.csdn.net/weixin_41010198/article/details/119698015

推送到npm

  • 切换npm镜像

npm config set registry http://registry.npmjs.org

  • 登录npm账号

npm login # 如果已经在官网有账号,可以直接登录

npm adduser # 创建 NPM 账号

npm whoami # 查看登录状态

输入npm login 遇到的坑:

npm ERR! code ETIMEDOUT
npm ERR! errno ETIMEDOUT
npm ERR! network request to http://registry.npmjs.org/-/v1/login failed, reason: connect ETIMEDOUT 104.16.17.35:80
npm ERR! network This is a problem related to network connectivity.
npm ERR! network In most cases you are behind a proxy or have bad network settings.
npm ERR! network
npm ERR! network If you are behind a proxy, please make sure that the
npm ERR! network 'proxy' config is set properly.  See: 'npm help config'

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\zeng.weidie\AppData\Roaming\npm-cache\_logs\2021-09-24T10_17_46_831Z-debug.log

把代理关掉

  • 发布npm新版本

npm publish

输入npm publish 遇到的坑

npm ERR! code E403
npm ERR! 403 403 Forbidden - PUT https://registry.npmjs.org/lanke-template-h5 - Forbidden
npm ERR! 403 In most cases, you or one of your dependencies are requesting
npm ERR! 403 a package version that is forbidden by your security policy.

npm ERR! A complete log of this run can be found in:
npm ERR!     D:\NodeJS\node_cache\_logs\2020-11-27T03_42_41_070Z-debug.log

解决方案:首次注册,没有验证邮箱,去邮箱按步验证,再次发布即可解决

  • 发布成功切换至淘宝镜像

npm config set registry https://registry.npm.taobao.org

测试发布是否成功

删除node_module下的react-native-videolineview-jm,在Terminal中输入 yarn add react-native-videolineview-jm ,或者配置主工程package.json,使用yarn install,这会把配置文件中的所有组件拉取一遍。

源码:https://github.com/JimiPlatform/react-native-videolineview-jm

 类似资料: