ReactNative原生Android UI组件拓展开发&发布

Author: Necfol

说明: 本文档用于指导前端React Native的原生Android UI组件开发&发布,如需开发其他其他框架应用,不适用本文档

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

RN官网上这么描述拓展原生UI组件开发封装,真的是日了狗,我在实践中遇到了不少问题,官网文档有些甚至是错误的,废话不多说,以下是依照官网示例中的imageview所做的实践教程。

1.原生组件环境

新建RN项目

按照官网步骤新建一个RN项目

1
$ react-native init example

新建好之后,使用Android Studio打开RN项目中android文件夹下的android工程

新建Android库

使用Android Studio新建Module,即File->New->New Module->Android Library。
Android Library

我们给静态库取名为NativeImageExample。

在Android/app中修改build.gradle,在dependencies中添加

1
compile project(':nativeimageexample')

这样我们的Android库能够关联到app工程下。
为了开发Android库,还需要其依赖react native,nativeimageexample/修改build.gradle,在dependencies中添加

1
compile "com.facebook.react:react-native:+"  // From node_modules

2.开发原生组件

在…/nativeimageexample/src/main/java/com/example/nativeimageexample文件夹下新建类NativeImageExampleManager,该类继承自类SimpleViewManager,
NewClass
NativeImageExampleManager

这个类必须实现两个方法,一个是覆写getName方法,它返回一个字符串名字,在JS中我们就使用这个名字调用这个模块;另外一个是构造方法NativeImageExampleManager,部分代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.example.nativeimageexample;

import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.views.image.ReactImageView;
import android.support.annotation.Nullable;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.ViewProps;
import com.facebook.react.views.image.ImageResizeMode;
/**
* Created by Necfol on 3/22/18.
*/

public class NativeImageExampleManager extends SimpleViewManager<ReactImageView> {
public static final String REACT_CLASS = "RCTImageView";
public NativeImageExampleManager(Object mCallerContext) {
this.mCallerContext = mCallerContext;
}
@Override
public String getName() {
return REACT_CLASS;
}
}

注意⚠️:此时Android Studio可能会提示错误:

1
2
3
4
Error:Execution failed for task ':app:processDebugManifest'.
> Manifest merger failed : Attribute application@allowBackup value=(false) from AndroidManifest.xml:11:7-34
is also present at [example:nativeimageexample:unspecified] AndroidManifest.xml:12:9-35 value=(true).
Suggestion: add 'tools:replace="android:allowBackup"' to <application> element at AndroidManifest.xml:7:5-24:19 to override.

这个解决办法是将…/nativeimageexample/src/main/AndroidManifest.xml中的allowBackup设置为false。
此时我们还不能访问此module,还需要创建实现ReactPackage的接口(interfaces)的NativeImageExamplePackage类。代码:
NativeImageExamplePackage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.example.nativeimageexample;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
* Created by Necfol on 3/22/18.
*/

public class NativeImageExamplePackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
List<ViewManager> viewManagers = Arrays.<ViewManager>asList(
new NativeImageExampleManager(reactContext)
);
return viewManagers;
}
}

修改…/android/app/src/main/java/com/example/MainApplication.java
文件中的getPackages方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
import com.example.nativeimageexample.NativeImageExamplePackage;
...

public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new NativeImageExamplePackage()
);
}
}

这个时候我们运行命令:

1
$ react-native run-android

你可能会惊喜的发现报错了❌

1
2
3
4
Error retrieving parent for item: 
No resource found that matches the given name 'android:
TextAppearance.Material.Widget.Button.Borderless.Colored'.
...

我们可以通过修改…/android/nativeimageexample/build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
android {
compileSdkVersion 23
buildToolsVersion '26.0.1'

defaultConfig {
minSdkVersion 16
targetSdkVersion 23
...
}
}
dependencies {
...
// compile 'com.android.support:appcompat-v7:26.+'
compile "com.android.support:appcompat-v7:23.0.1"
...
}

这个方法不一定对,是我自己猜测之后修改的。😅

这个时候,我们在js中可以访问一下我们的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { requireNativeComponent} from 'react-native';
...
let NativeUIModuleExample = requireNativeComponent('RCTImageView', null);
...
export default class App extends Component<Props> {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<NativeUIModuleExample src={[{uri:'http://f.named.cn/f/45de9752d88841c87da0f23f8a798a4c.jpg'}]} style={styles.test}/>
<Text style={styles.instructions}>
To get started, edit App.js
</Text>
<Text style={styles.instructions}>
{instructions}
</Text>
</View>
);
}
}
...

注意给样式加上宽高。

end

3.发布

接下来就是我们熟悉的发布npm了

在github上创建一个仓库react-native-NativeImageModuleExample,clone到本地创建android文件夹

1
2
3
$ git clone git@github.com:necfol/react-native-NativeImageModuleExample.git
$ cd react-native-NativeImageModuleExample
$ mkdir android

拷贝我们之前创建的…/android/nativeimageexample文件夹下的所有内容

1
$ cp -R ../../../../androidtest/example/android/nativeimageexample/* ~Necfol/focus/newcrovapp/androidtest/npm/react-native-NativeImageModuleExample/android

在react-native-NativeImageModuleExample目录下创建一个index.js,它是整个原生模块的入口,我们这里只是将原生进行导出

1
2
3
4
5
6
7
8
/**
* Created by Necfol on 3/22/18.
*/
import {
requireNativeComponent
} from 'react-native';
var RCTImageView = requireNativeComponent('RCTImageView', null);
module.exports = RCTImageView;

在react-native-NativeImageModuleExample文件夹下运行命令

1
$ npm init

输入相应的信息,package.json就创建完成了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"name": "react-native-nativeimagemoduleexample",
"version": "1.0.0",
"description": "my Android RN native module demo",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/necfol/react-native-NativeImageModuleExample.git"
},
"author": "Necfol",
"license": "ISC",
"bugs": {
"url": "https://github.com/necfol/react-native-NativeImageModuleExample/issues"
},
"homepage": "https://github.com/necfol/react-native-NativeImageModuleExample#readme"
}

接下来我们就可以

1
npm publish

4.使用

新建一个RN项目,依次运行:

1
2
3
$ npm install --save react-native-nativeimagemoduleexample
$ npm install
$ react-native link

在App.js中新增代码:

1
2
3
import NativeImageModule from 'react-native-nativeimagemoduleexample';
<!--在render中-->
<NativeImageModule src={[{uri:'http://f.named.cn/f/45de9752d88841c87da0f23f8a798a4c.jpg'}]} style={styles.image}/>

运行:

1
$ react-native run-android

end2
Demo可参考示例

官方文档中还有属性,事件之类用法,暂不做介绍。

分享到