包装第三方本地库

编辑

学习如何使用 Expo Modules API 创建一个简单的包装器,围绕两个独立的本地库。


Expo 模块使得在 React Native 项目中轻松使用为 Android 和 iOS 构建的本地外部库成为可能。本教程重点在于利用 Expo 模块 API 创建径向图,使用两个在这两个本地平台上都可以访问的类似库。

iOS 库受 Android 库的启发,因此它们都具有类似的 API 和功能。这使它们成为本教程的好例子。

如何包装本地库
如何包装本地库

在本视频中,您将学习如何使用 Expo Modules API 包装本地库。


1

创建新模块

以下步骤假设新模块是在新的 Expo 项目中创建的。然而,您可以通过遵循替代说明在现有项目中创建新模块。

另外,您可以将新模块作为视图使用,放在现有 Expo 项目目录中。在项目目录中运行以下命令:

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

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

通过运行以下命令创建一个新的空 Expo 模块,可以在 npm 上发布并在任何 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 文件,将本地依赖添加到模块中:

android/build.gradle
dependencies { implementation project(':expo-modules-core') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}" + implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' }
ios/ExpoRadialChart.podspec
s.static_framework = true s.dependency 'ExpoModulesCore' + s.dependency 'DGCharts', '~> 5.1.0' # Swift/Objective-C 兼容性
你是否尝试使用 .aar 依赖?

android 目录下,创建另一个名为 libs 的目录,并将 .aar 文件放入其中。然后,从自动链接中添加该文件作为 Gradle 项目:

expo-module.config.json
"android": { + "gradleAarProjects": [ + { + "name": "test-aar", + "aarFilePath": "android/libs/test.aar" + } + ], "modules": [

最后,使用 ${project.name}$ 前缀将依赖项添加到 android/build.gradle 文件中的 dependencies 列表中:

android/build.gradle
dependencies { implementation project(':expo-modules-core') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}" + implementation project(":${project.name}\$test-aar") }

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

android/build.gradle
repositories { mavenCentral() + flatDir { + dirs 'libs' + } }

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

android/build.gradle
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 配置选项来使用捆绑为框架的依赖项。

ios/ExpoRadialChart.podspec
s.static_framework = true s.dependency 'ExpoModulesCore' + s.vendored_frameworks = 'Frameworks/MyFramework.framework' # Swift/Objective-C 兼容性
注意:用来指定框架路径的文件模式是相对于 podspec 文件的,并且不支持遍历父目录 (..),这意味着您需要将框架放置在 ios 目录中(或 ios 的子目录中)。

框架添加后,请确保 source_files 选项的文件模式不匹配框架内的任何文件。一种方法是将您的 iOS 源 Swift 文件(即 ExpoRadialChartView.swiftExpoRadialChartModule.swift)移动到与您放置框架的目录分开的 src 目录,并将 source_files 选项更新为仅匹配 src 目录:

ios/ExpoRadialChart.podspec
- s.source_files = '**/*.{h,m,mm,swift,hpp,cpp}' + s.source_files = 'src/**/*.{h,m,mm,swift,hpp,cpp}'

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

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

4

定义 API

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

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. 定义一个 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. 创建一个 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

编写示例应用以使用模块

您可以更新 app 目录中的应用来测试模块。使用 ExpoRadialChartView 组件渲染一个有三个切片的饼图:

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

重建并启动您的应用程序

为了确保您的应用在两个平台上都能成功构建,请从步骤 2 中重新运行构建命令。在任何平台成功构建应用后,您将看到一个有三个切片的饼图:

恭喜!您已经使用 Expo 模块 API 创建了围绕两个独立第三方本地库的第一个简单包装器。

下一步

Expo 模块 API 参考

一个参考,用于使用 Kotlin 和 Swift 创建本地模块。