包装第三方本地库
编辑
学习如何使用 Expo Modules API 创建一个简单的包装器,围绕两个独立的本地库。
Expo 模块使得在 React Native 项目中轻松使用为 Android 和 iOS 构建的本地外部库成为可能。本教程重点在于利用 Expo 模块 API 创建径向图,使用两个在这两个本地平台上都可以访问的类似库。
iOS 库受 Android 库的启发,因此它们都具有类似的 API 和功能。这使它们成为本教程的好例子。

在本视频中,您将学习如何使用 Expo Modules API 包装本地库。
1
创建新模块
以下步骤假设新模块是在新的 Expo 项目中创建的。然而,您可以通过遵循替代说明在现有项目中创建新模块。
另外,您可以将新模块作为视图使用,放在现有 Expo 项目目录中。在项目目录中运行以下命令:
- npx create-expo-module --local expo-radial-chart现在,打开新创建的 modules/expo-radial-chart 目录以开始编辑本地代码。
通过运行以下命令创建一个新的空 Expo 模块,可以在 npm 上发布并在任何 Expo 应用中使用:
- npx create-expo-module expo-radial-chart提示:如果您不打算发布此库,请在终端窗口中对所有提示按 return 接受默认值。
现在,打开新创建的 expo-radial-chart 目录以开始编辑本地代码。
2
运行示例项目
为了验证一切功能正常,让我们运行示例项目。
如果您是从现有的 Expo 项目开始的,请从 Expo 项目的根目录运行以下命令:
# 在 Android 上运行 example-expo-app- npx expo run:android# 在 iOS 上运行示例应用- npx expo run:ios如果您是从新模块项目开始的,打开终端窗口,启动 TypeScript 编译器以监视更改,并重建模块 JavaScript:
# 确保在运行以下命令之前位于 expo-radial-chart 目录中- npm run build在另一个终端窗口中,编译并运行示例应用:
- cd example-expo-app# 在 Android 上运行 example-expo-app- npx expo run:android# 在 iOS 上运行示例应用- npx expo run:ios3
添加本地依赖
通过编辑 expo-radial-chart/android/build.gradle 和 expo-radial-chart/ios/ExpoRadialChart.podspec 文件,将本地依赖添加到模块中:
dependencies { implementation project(':expo-modules-core') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}" + implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' }
s.static_framework = true s.dependency 'ExpoModulesCore' + s.dependency 'DGCharts', '~> 5.1.0' # Swift/Objective-C 兼容性
你是否尝试使用 .aar 依赖?
在 android 目录下,创建另一个名为 libs 的目录,并将 .aar 文件放入其中。然后,从自动链接中添加该文件作为 Gradle 项目:
"android": { + "gradleAarProjects": [ + { + "name": "test-aar", + "aarFilePath": "android/libs/test.aar" + } + ], "modules": [
最后,使用 ${project.name}$ 前缀将依赖项添加到 android/build.gradle 文件中的 dependencies 列表中:
dependencies { implementation project(':expo-modules-core') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}" + implementation project(":${project.name}\$test-aar") }
在 android 目录下,创建另一个名为 libs 的目录,并将 .aar 文件放入其中。然后,将该目录作为一个存储库添加:
repositories { mavenCentral() + flatDir { + dirs 'libs' + } }
最后,将依赖项添加到 dependencies 列表中。使用包路径,而不是文件名,包路径在结尾包含 @aar:
dependencies { implementation project(':expo-modules-core') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}" + implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0@aar' }
你是否尝试使用 .xcframework 或 .framework 依赖?
在 iOS 上,您还可以通过使用 vendored_frameworks 配置选项来使用捆绑为框架的依赖项。
s.static_framework = true s.dependency 'ExpoModulesCore' + s.vendored_frameworks = 'Frameworks/MyFramework.framework' # Swift/Objective-C 兼容性
注意:用来指定框架路径的文件模式是相对于 podspec 文件的,并且不支持遍历父目录 (..),这意味着您需要将框架放置在 ios 目录中(或 ios 的子目录中)。
框架添加后,请确保 source_files 选项的文件模式不匹配框架内的任何文件。一种方法是将您的 iOS 源 Swift 文件(即 ExpoRadialChartView.swift 和 ExpoRadialChartModule.swift)移动到与您放置框架的目录分开的 src 目录,并将 source_files 选项更新为仅匹配 src 目录:
- s.source_files = '**/*.{h,m,mm,swift,hpp,cpp}' + s.source_files = 'src/**/*.{h,m,mm,swift,hpp,cpp}'
您的 ios 目录应最终具有类似于以下的文件结构:
FrameworksMyFramework.frameworksrcExpoRadialChartView.swiftExpoRadialChartModule.swiftExpoRadialChart.podspec4
定义 API
要在应用程序中使用模块,请为 props 定义类型。它接受一个系列列表 — 每个系列都有一个颜色和一个百分比值。
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 文件:
import * as React from 'react'; export default function ExpoRadialChartView() { return <div>未实现</div>; }
5
在 Android 上实现模块
现在您可以通过编辑占位符文件,使用以下更改来实现本地功能:
- 创建一个
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 prop,并在 prop 更改时调用本地 setChartData 函数:
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 上实现模块
现在您可以通过编辑占位符文件,使用以下更改来实现本地功能:
- 创建一个新的
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 prop,并在 prop 更改时调用本地 setChartData 函数:
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
编写示例应用以使用模块
您可以更新 app 目录中的应用来测试模块。使用 ExpoRadialChartView 组件渲染一个有三个切片的饼图:
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';
恭喜!您已经使用 Expo 模块 API 创建了围绕两个独立第三方本地库的第一个简单包装器。
下一步
一个参考,用于使用 Kotlin 和 Swift 创建本地模块。