包装第三方原生库

编辑页面

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


For the complete documentation index, see llms.txt. Use this Use this file to discover all available pages.

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

iOS 库受到 Android 库的启发,因此它们都具有相似的 API 和功能。这使它们成为本教程的一个很好的示例。

如何包装原生库
如何包装原生库

在这个视频中,你将学习如何使用 Expo Modules API 包装原生库。


1

创建一个新模块

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

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

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

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

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

Terminal
npx create-expo-module expo-radial-chart
提示:如果你不打算发布这个库,请在终端窗口中对所有提示按 return 以接受默认值。

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

2

运行示例项目

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

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

Terminal
# 在 Android 上运行 example-expo-app
npx expo run:android

# 在 iOS 上运行示例应用
npx expo run:ios

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

Terminal
# 在运行下面的命令之前,请确保你位于 expo-radial-chart 目录中
npm run build

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

Terminal
cd example-expo-app

# 在 Android 上运行 example-expo-app
npx expo run:android

# 在 iOS 上运行示例应用
npx expo run:ios

3

添加原生依赖

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

你是否想使用 .aar 依赖?

android 目录中,再创建一个名为 libs 的目录,并将 .aar 文件放入其中。然后,通过 autolinking 将该文件作为 Gradle 项目添加:

最后,将依赖添加到 android/build.gradle 文件中的 dependencies 列表里,并使用依赖指定的名称,前面加上 ${project.name}$ 前缀:

android 目录中,再创建一个名为 libs 的目录,并将 .aar 文件放入其中。然后,将该目录添加为仓库:

最后,将依赖添加到 dependencies 列表中。不要使用文件名,而是使用包路径,并在末尾加上 @aar

你是否想使用 .xcframework.framework 依赖?

在 iOS 上,你也可以通过使用 vendored_frameworks 配置选项来使用以 framework 形式打包的依赖。

注意:用于指定 framework 路径的文件模式是相对于 podspec 文件的, 并且不支持遍历父目录(..),这意味着你需要将 framework 放在 ios 目录 中(或 ios 的子目录中)。

添加 framework 后,请确保 source_files 选项的文件模式不会匹配 framework 中的任何文件。实现这一点的一种方法是将 iOS 源 Swift 文件(即 ExpoRadialChartView.swiftExpoRadialChartModule.swift)移动到一个与放置 framework 的位置分开的 src 目录中,并将 source_files 选项更新为只匹配 src 目录:

你的 ios 目录最终应具有类似以下的文件结构:

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

4

定义一个 API

为了在应用中使用该模块,请定义 props 的类型。它接受一个 series 列表——每个 series 都包含一个颜色和一个百分比值。

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[]; };

由于本示例中该模块没有为 web 实现,我们将替换 src/ExpoRadialChartView.web.tsx 文件:

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

5

在 Android 上实现模块

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

  1. 创建一个 PieChart 实例,并将其 layoutParams 设置为与父视图一致。然后,使用 addView 函数将其添加到视图层级中。
  2. 定义一个接受 Series 对象列表的 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 prop,并在 prop 变化时调用原生的 setChartData 函数:

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 上实现模块

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

  1. 创建一个新的 PieChartView 实例,并使用 addSubview 函数将其添加到视图层级中。
  2. 设置 clipsToBounds 属性,并重写 layoutSubviews 函数,以确保图表视图始终与父视图大小相同。
  3. 创建一个接受 series 列表的 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 prop,并在 prop 变化时调用原生的 setChartData 函数:

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

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

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

src/app/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

重新构建并启动你的应用

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

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

下一步

Expo 模块 API 参考

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