封装第三方原生库
了解如何使用 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:
- 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:
- 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:
# 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:
# 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:
- cd example-expo-app# Run the example-expo-app on Android- npx expo run:android# Run the example app on iOS- npx expo run:ios3
添加本地依赖
🌐 Add native dependencies
通过编辑 expo-radial-chart/android/build.gradle 和 expo-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:
FrameworksMyFramework.frameworksrcExpoRadialChartView.swiftExpoRadialChartModule.swiftExpoRadialChart.podspec4
定义 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.
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:
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:
- 创建一个
PieChart实例,并将其layoutParams设置为与父视图匹配。然后,使用addView函数将其添加到视图层次结构中。 - 定义一个
setChartData函数,该函数接受一个Series对象列表。你可以遍历该列表,为每个系列创建一个PieEntry,并将颜色存储在一个单独的列表中。 - 创建一个
PieDataSet,使用它来创建一个PieData对象,并将其设置为PieChart实例的数据。
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:
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:
- 创建一个新的
PieChartView实例,并使用addSubview函数将其添加到视图层级中。 - 设置
clipsToBounds属性并重写layoutSubviews函数,以确保图表视图始终与父视图大小相同。 - 创建一个
setChartData函数,该函数接受一个系列列表,使用数据创建一个PieChartDataSet实例,并将其分配给PieChartView实例的data属性。
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:
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:
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
创建使用 Kotlin 和 Swift 的原生模块的参考资料。