封装第三方原生库

了解如何使用 Expo Modules API 为两个单独的原生库创建一个简单的封装器。


Expo 模块使在 React Native 项目中轻松使用为 Android 和 iOS 构建的本地外部库成为可能。本教程重点介绍如何利用 Expo 模块 API,使用两个可在本地平台上访问的相似库来创建径向图表。

🌐 Expo modules make it possible to easily use native, external libraries built for Android and iOS in React Native projects. This tutorial focuses on utilizing the Expo Modules API to create radial charts using two similar libraries accessible on both native platforms.

iOS 库的灵感来自 Android 库,因此它们都有类似的 API 和功能。这使它们成为本教程的一个很好的示例。

🌐 The iOS library is inspired by the Android library, so they both have similar API and functionality. This makes them a good example for this tutorial.

如何封装本地库
如何封装本地库

在本视频中,你将学习如何使用 Expo 模块 API 封装原生库。


1

创建新模块

🌐 Create a new module

以下步骤假设新模块是在一个新的 Expo 项目中创建的。不过,你也可以按照备用说明在现有项目中创建新模块。

🌐 The following steps assume that the new module is created inside a new Expo project. However, you can create a new module inside an existing project by following the alternative instructions.

或者,你可以将新模块作为视图使用在现有的 Expo 项目目录中。在你的项目目录中运行以下命令:

🌐 Alternatively, you can use the new module as a view inside the existing Expo project directory. Run the following command in your project's directory:

Terminal
npx create-expo-module --local expo-radial-chart

现在,打开新创建的 modules/expo-radial-chart 目录开始编辑本地代码。

🌐 Now, open the newly created modules/expo-radial-chart directory to start editing the native code.

通过运行以下命令创建一个可以发布到 npm 并在任何 Expo 应用中使用的新空 Expo 模块:

🌐 Create a new empty Expo module that can be published on npm and utilized in any Expo app by running the following command:

Terminal
npx create-expo-module expo-radial-chart

信息 提示:如果你不打算发布这个库,在终端窗口中对所有提示按 回车 即可接受默认值。

现在,打开新创建的 expo-radial-chart 目录开始编辑本地代码。

🌐 Now, open the newly created expo-radial-chart directory to start editing the native code.

2

运行示例项目

🌐 Run the example project

为了验证一切是否正常运行,我们来运行示例项目。

🌐 To verify that everything is functioning correctly, let's run the example project.

如果你是从现有的 Expo 项目开始的,请在你的 Expo 项目的根目录下运行以下命令:

🌐 If you started with an existing Expo project, run the following commands from your Expo project's root directory:

Terminal
# Run the example-expo-app on Android
npx expo run:android

# Run the example app on iOS
npx expo run:ios

如果你是从一个新的模块项目开始,打开终端窗口,启动 TypeScript 编译器以监视更改,然后重新构建模块的 JavaScript:

🌐 If you started with a new module project, open a terminal window, start the TypeScript compiler to watch for changes, and rebuild the module JavaScript:

Terminal
# Ensure you are inside expo-radial-chart directory before running the command below
npm run build

在另一个终端窗口中,编译并运行示例应用:

🌐 In another terminal window, compile and run the example app:

Terminal
cd example-expo-app

# Run the example-expo-app on Android
npx expo run:android

# Run the example app on iOS
npx expo run:ios

3

添加本地依赖

🌐 Add native dependencies

通过编辑 expo-radial-chart/android/build.gradleexpo-radial-chart/ios/ExpoRadialChart.podspec 文件,将本地依赖添加到模块中:

🌐 Add the native dependencies to the module by editing the expo-radial-chart/android/build.gradle and expo-radial-chart/ios/ExpoRadialChart.podspec files:

Are you trying to use a .aar dependency?

Inside the android directory, create another directory called libs and place the .aar file inside it. Then, add the file as a Gradle project from autolinking:

Finally, add the dependency to the dependencies list in the android/build.gradle file, using the dependency's specified name with ${project.name}$ prefix:

Inside the android directory, create another directory called libs and place the .aar file inside it. Then, add the directory as a repository:

Finally, add the dependency to the dependencies list. Instead of the filename, use the package path, which includes the @aar at the end:

Are you trying to use an .xcframework or .framework dependency?

On iOS, you can also use dependencies bundled as a framework by using the vendored_frameworks config option.

Note: The file pattern used to specify the path to the framework is relative to the podspec file, and doesn't support traversing the parent directory (..), meaning you need to place the framework inside the ios directory (or a subdirectory of ios).

Once the framework is added, make sure that the source_files option file pattern doesn't match any files inside the framework. One way to achieve this is to move your iOS source Swift files (that is ExpoRadialChartView.swift and ExpoRadialChartModule.swift) into a src directory separate from where you placed your framework(s) and update the source_files option to only match the src directory:

Your ios directory should end up with a file structure similar to this:

Frameworks
MyFramework.framework
src
ExpoRadialChartView.swift
ExpoRadialChartModule.swift
ExpoRadialChart.podspec

4

定义 API

🌐 Define an API

要在应用中使用该模块,需为属性定义类型。它接受一个系列列表——每个系列都有一个颜色和一个百分比值。

🌐 To use the module in the app, define the types for the props. It accepts a list of series — each with a color and a percentage value.

src/ExpoRadialChart.types.ts
import { ViewStyle } from 'react-native/types'; export type ChangeEventPayload = { value: string; }; type Series = { color: string; percentage: number; }; export type ExpoRadialChartViewProps = { style?: ViewStyle; data: Series[]; };

由于在此示例中该模块尚未在网页上实现,我们来替换 src/ExpoRadialChartView.web.tsx 文件:

🌐 Since the module isn't implemented for web in this example, let's replace the src/ExpoRadialChartView.web.tsx file:

src/ExpoRadialChartView.web.tsx
import * as React from 'react'; export default function ExpoRadialChartView() { return <div>Not implemented</div>; }

5

在安卓上实现该模块

🌐 Implement the module on Android

现在你可以通过编辑占位文件并进行以下更改来实现本地功能:

🌐 Now you can implement the native functionality by editing the placeholder files with the following changes:

  1. 创建一个 PieChart 实例,并将其 layoutParams 设置为与父视图匹配。然后,使用 addView 函数将其添加到视图层次结构中。
  2. 定义一个 setChartData 函数,该函数接受一个 Series 对象列表。你可以遍历该列表,为每个系列创建一个 PieEntry,并将颜色存储在一个单独的列表中。
  3. 创建一个 PieDataSet,使用它来创建一个 PieData 对象,并将其设置为 PieChart 实例的数据。
android/src/main/java/expo/modules/radialchart/ExpoRadialChartView.kt
package expo.modules.radialchart import android.content.Context import android.graphics.Color import androidx.annotation.ColorInt import com.github.mikephil.charting.charts.PieChart import com.github.mikephil.charting.data.PieData import com.github.mikephil.charting.data.PieDataSet import com.github.mikephil.charting.data.PieEntry import expo.modules.kotlin.AppContext import expo.modules.kotlin.records.Field import expo.modules.kotlin.records.Record import expo.modules.kotlin.views.ExpoView class Series : Record { @Field val color: String = "#ff0000" @Field val percentage: Float = 0.0f } class ExpoRadialChartView(context: Context, appContext: AppContext) : ExpoView(context, appContext) { internal val chartView = PieChart(context).also { it.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) addView(it) } fun setChartData(data: ArrayList<Series>) { val entries: ArrayList<PieEntry> = ArrayList() val colors: ArrayList<Int> = ArrayList() for (series in data) { entries.add(PieEntry(series.percentage)) colors.add(Color.parseColor(series.color)) } val dataSet = PieDataSet(entries, "DataSet"); dataSet.colors = colors; val pieData = PieData(dataSet); chartView.data = pieData; chartView.invalidate(); } }

你还需要使用 Prop 函数来定义 data 属性,并在属性发生变化时调用原生 setChartData 函数:

🌐 You also need to use the Prop function to define the data prop and call the native setChartData function when the prop changes:

android/src/main/java/expo/modules/radialchart/ExpoRadialChartModule.kt
package expo.modules.radialchart import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition class ExpoRadialChartModule : Module() { override fun definition() = ModuleDefinition { Name("ExpoRadialChart") View(ExpoRadialChartView::class) { Prop("data") { view: ExpoRadialChartView, prop: ArrayList<Series> -> view.setChartData(prop); } } } }

6

在 iOS 上实现该模块

🌐 Implement the module on iOS

现在你可以通过编辑占位文件并进行以下更改来实现本地功能:

🌐 Now you can implement the native functionality by editing the placeholder files with the following changes:

  1. 创建一个新的 PieChartView 实例,并使用 addSubview 函数将其添加到视图层级中。
  2. 设置 clipsToBounds 属性并重写 layoutSubviews 函数,以确保图表视图始终与父视图大小相同。
  3. 创建一个 setChartData 函数,该函数接受一个系列列表,使用数据创建一个 PieChartDataSet 实例,并将其分配给 PieChartView 实例的 data 属性。
ios/ExpoRadialChartView.swift
import ExpoModulesCore import DGCharts struct Series: Record { @Field var color: UIColor = UIColor.black @Field var percentage: Double = 0 } class ExpoRadialChartView: ExpoView { let chartView = PieChartView() required init(appContext: AppContext? = nil) { super.init(appContext: appContext) clipsToBounds = true addSubview(chartView) } override func layoutSubviews() { chartView.frame = bounds } func setChartData(data: [Series]) { let set1 = PieChartDataSet(entries: data.map({ (series: Series) -> PieChartDataEntry in return PieChartDataEntry(value: series.percentage) })) set1.colors = data.map({ (series: Series) -> UIColor in return series.color }) let chartData: PieChartData = [set1] chartView.data = chartData } }

你还需要使用 Prop 函数来定义 data 属性,并在属性发生变化时调用原生 setChartData 函数:

🌐 You also need to use the Prop function to define the data prop and call the native setChartData function when the prop changes:

ios/ExpoRadialChartModule.swift
import ExpoModulesCore public class ExpoRadialChartModule: Module { public func definition() -> ModuleDefinition { Name("ExpoRadialChart") View(ExpoRadialChartView.self) { Prop("data") { (view: ExpoRadialChartView, prop: [Series]) in view.setChartData(data: prop) } } } }

7

编写一个示例应用来使用该模块

🌐 Write an example app to use the module

你可以在 app 目录下更新应用来测试该模块。使用 ExpoRadialChartView 组件来渲染一个包含三个扇区的饼图:

🌐 You can update the app inside the app directory to test the module. Use the ExpoRadialChartView component to render a pie chart with three slices:

app/(tabs)/index.tsx
import { ExpoRadialChartView } from '@/modules/expo-radial-chart'; import { StyleSheet } from 'react-native'; export default function App() { return ( <ExpoRadialChartView style={styles.container} data={[ { color: '#ff0000', percentage: 0.5, }, { color: '#00ff00', percentage: 0.2, }, { color: '#0000ff', percentage: 0.3, }, ]} /> ); } const styles = StyleSheet.create({ container: { flex: 1, }, });

信息 提示:如果你创建了一个新模块,请确保将导入语句更新为:import { ExpoRadialChartView } from 'expo-radial-chart';

8

重建并启动你的应用

🌐 Rebuild and launch your application

为了确保你的应用在两个平台上都能成功构建,请重新运行第 2 步中的构建命令。应用在任一平台成功构建后,你将看到一个包含三块的饼图:

🌐 To make sure your app builds successfully on both platforms, rerun the build commands from step 2. After the app is successfully built on any of the platform you'll see a pie chart with three slices:

恭喜!你已经使用 Expo Modules API 创建了第一个简单的封装,围绕两个独立的第三方原生库。

🌐 Congratulations! You have created your first simple wrapper around two separate third-party native libraries using Expo Modules API.

下一步

🌐 Next step

Expo 模块 API 参考

创建使用 Kotlin 和 Swift 的原生模块的参考资料。