模块 API 参考
编辑
Expo 模块 API 的 API 参考。
原生模块 API 是建立在 JSI 及其他 React Native 构建的低级原语之上的一个抽象层。它使用现代语言(Swift 和 Kotlin)构建,并提供一个易于使用且方便的 API,使其在可能的情况下跨平台一致。
定义组件
正如您可能在 入门 页面上的片段中注意到的,每个模块类必须实现 definition 函数。
模块定义由描述模块功能和行为的 DSL 组件组成。
名称
设置 JavaScript 代码将用于引用模块的模块名称。接受一个字符串作为参数。这可以从模块的类名推导出来,但建议为了清晰起见明确设置。
Name("MyModuleName")
常量
在 JavaScript 对象上定义一个常量属性。该属性仅在首次访问时计算,后续访问将返回缓存值。
Constant("PI") { Double.pi }
Constant("PI") { Math.PI }
函数
定义一个将导出到 JavaScript 的本机同步函数。同步意味着当函数在 JavaScript 中执行时,它的本机代码在同一线程上运行,并阻止脚本的进一步执行,直到本机函数返回。
参数
- name:
String— 您将从 JavaScript 中调用的函数名称。 - body:
(args...) -> ReturnType— 函数调用时要运行的闭包。
该函数最多可以接收 8 个参数。这是由于 Swift 和 Kotlin 中的泛型限制,因为此组件必须为每种参数数目单独实现。
有关可以在函数体中使用的类型的更多详细信息,请参见 参数类型 部分。
Function("mySyncFunction") { (message: String) in return message }
Function("mySyncFunction") { message: String -> return@Function message }
import { requireNativeModule } from 'expo-modules-core'; // 假设我们将模块命名为 "MyModule" const MyModule = requireNativeModule('MyModule'); function getMessage() { return MyModule.mySyncFunction('bar'); }
AsyncFunction
定义一个始终返回 Promise 的 JavaScript 函数,并且默认情况下其本机代码将在与 JavaScript 运行时不同的线程上调度。
参数
- name:
String— 您将从 JavaScript 中调用的函数名称。 - body:
(args...) -> ReturnType— 函数调用时要运行的闭包。
如果最后一个参数的类型是 Promise,该函数将在响应返回给 JavaScript 之前等待该 Promise 被解析或拒绝。否则,函数将立即用返回值解析或在抛出异常时拒绝。
该函数最多可以接收 8 个参数(包括 Promise)。
有关可以在函数体中使用的类型的更多详细信息,请参见 参数类型 部分。
当 AsyncFunction 在以下情况下更建议使用:
- 执行 I/O 绑定任务,例如发送网络请求或与文件系统交互
- 需要在不同线程上运行,例如,UI 相关任务的主 UI 线程
- 是大量或长时间运行的操作,这将阻塞 JavaScript 线程,从而减少应用程序的响应性
AsyncFunction("myAsyncFunction") { (message: String) in return message } // 或 AsyncFunction("myAsyncFunction") { (message: String, promise: Promise) in promise.resolve(message) }
AsyncFunction("myAsyncFunction") { message: String -> return@AsyncFunction message } // 或 // 确保从 `expo.modules.kotlin` 导入 `Promise` 类,而不是 `expo.modules.core`。 AsyncFunction("myAsyncFunction") { message: String, promise: Promise -> promise.resolve(message) }
import { requireNativeModule } from 'expo-modules-core'; // 假设我们将模块命名为 "MyModule" const MyModule = requireNativeModule('MyModule'); async function getMessageAsync() { return await MyModule.myAsyncFunction('bar'); }
可以通过在该组件的结果上调用 .runOnQueue 函数来更改 AsyncFunction 的本机队列。
AsyncFunction("myAsyncFunction") { (message: String) in return message }.runOnQueue(.main)
AsyncFunction("myAsyncFunction") { message: String -> return@AsyncFunction message }.runOnQueue(Queues.MAIN)
Kotlin 协程 Android
AsyncFunction 可以在 Android 上接收可挂起的主体。然而,它必须在 Coroutine 块后用中缀语法传入。您可以在 协程概述 上了解有关可挂起函数和协程的更多信息。
带有可挂起主体的 AsyncFunction 不能接收 Promise 作为参数。它使用挂起机制来执行异步调用。
函数将立即解析为提供的可挂起块的返回值,或在抛出异常时拒绝。该函数最多可以接收 8 个参数。
默认情况下,挂起函数在模块的协程范围内调度。此外,从主体块调用的每个其他可挂起函数都在同一范围内运行。 该范围的生命周期与模块的生命周期绑定 - 当模块被解除分配时,所有未完成的挂起函数将被取消。
AsyncFunction("suspendFunction") Coroutine { message: String -> // 您可以在此处执行其他可挂起函数。 // 例如,您可以使用 `kotlinx.coroutines.delay` 延迟解析底层 promise。 delay(5000) return@Coroutine message }
事件
定义模块可以发送到 JavaScript 的事件名称。
Events("onCameraReady", "onPictureSaved", "onBarCodeScanned")
Events("onCameraReady", "onPictureSaved", "onBarCodeScanned")
请参见 发送事件 了解如何从本机代码发送事件到 JavaScript/TypeScript。
属性
在表示原生模块的 JavaScript 对象上直接定义一个新属性。它与在模块对象上调用 Object.defineProperty 是相同的。
要声明只读属性,您可以使用需要两个参数的简写语法:
- name:
String— 您将从 JavaScript 使用的属性名称。 - getter:
() -> PropertyType— 当调用属性的 getter 时要运行的闭包。
Property("foo") { return "bar" }
Property("foo") { return@Property "bar" }
在可变属性的情况下,getter 和 setter 闭包都是必需的(使用下面的语法也可以声明仅具有 setter 的属性):
- name:
String— 您将从 JavaScript 使用的属性名称。 - getter:
() -> PropertyType— 当调用属性的 getter 时要运行的闭包。 - setter:
(newValue: PropertyType) -> void— 当调用属性的 setter 时要运行的闭包。
Property("foo") .get { return "bar" } .set { (newValue: String) in // 对新值执行某些操作 }
Property("foo") .get { return@get "bar" } .set { newValue: String -> // 对新值执行某些操作 }
import { requireNativeModule } from 'expo-modules-core'; // 假设我们将模块命名为 "MyModule" const MyModule = requireNativeModule('MyModule'); // 获取属性值 MyModule.foo; // 设置新值 MyModule.foo = 'foobar';
视图
使模块能够作为本机视图使用。视图定义中接受的定义组件:Prop, Events, GroupView 和 AsyncFunction。
在视图定义中的 AsyncFunction 将添加到表示本机视图的 React 组件的 React 引用。
此类异步函数会自动将本机视图的实例作为第一个参数接收,默认在 UI 线程上运行。
参数
- viewType — 将被渲染的本机视图的类。注意:在 Android 上,提供的类必须继承自
ExpoView,在 iOS 上是可选的。请参见扩展 ExpoView。 - definition:
() -> ViewDefinition— 视图定义的构建器。
View(UITextView.self) { Prop("text") { %%placeholder-start%%... %%placeholder-end%% } AsyncFunction("focus") { (view: UITextView) in view.becomeFirstResponder() } }
View(TextView::class) { Prop("text") { %%placeholder-start%%... %%placeholder-end%% } AsyncFunction("focus") { view: TextView -> view.requestFocus() } }
信息 支持 SwiftUI 视图的渲染尚在计划中。目前,您可以使用
UIHostingController并将其内容视图添加到您的 UIKit 视图。
OnAppEntersForeground
定义当应用即将进入前台模式时调用的监听器。
注意: 此函数在 Android 上不可用 — 您可能想要使用
OnActivityEntersForeground。
OnAppEntersBackground
定义当应用进入背景模式时调用的监听器。
注意: 此函数在 Android 上不可用 — 您可能想要使用
OnActivityEntersBackground代替。
OnAppBecomesActive
定义当应用再次变为活动状态时调用的监听器(在 OnAppEntersForeground 之后)。
注意: 此函数在 Android 上不可用 — 您可能想要使用
OnActivityEntersForeground。
OnActivityEntersForeground
定义活动生命周期监听器,该监听器在活动恢复后立即调用。
注意: 此函数在 iOS 上不可用 — 您可能想要使用
OnAppEntersForeground替代。
OnActivityEntersBackground
定义活动生命周期监听器,该监听器在活动暂停后立即调用。
注意: 此函数在 iOS 上不可用 — 您可能想要使用
OnAppEntersBackground代替。
OnActivityDestroys
定义活动生命周期监听器,该监听器在拥有 JavaScript 上下文的活动即将被销毁时调用。
注意: 此函数在 iOS 上不可用 — 您可能想要使用
OnAppEntersBackground代替。
OnActivityResult
定义活动生命周期监听器,该监听器在使用 startActivityForResult 启动的活动返回结果时调用。
参数
- activity — 收到结果的 Android 活动。
- payload — 一个包含活动结果数据的对象。
- requestCode:
Int— 最初提供给startActivityForResult的请求代码,用于识别结果的来源。 - resultCode:
Int— 子活动返回的结果代码(例如,Activity.RESULT_OK或Activity.RESULT_CANCELED)。 - data — 可选的意图,携带从启动的活动返回的结果数据。可以为
null。
- requestCode:
AsyncFunction('someFunc') { %%placeholder-start%%... %%placeholder-end%% activity.startActivityForResult(someIntent, SOME_REQUEST_CODE) } OnActivityResult { activity, payload -> %%placeholder-start%%... %%placeholder-end%% }
常量
警告 已弃用: 请改用
Constant。
在模块上设置常量属性。可以接受字典或返回字典的闭包。
// 从字典创建 Constants([ "PI": Double.pi ]) // 或由闭包返回 Constants { return [ "PI": Double.pi ] }
// 作为参数传递 Constants( "PI" to kotlin.math.PI ) // 或由闭包返回 Constants { return@Constants mapOf( "PI" to kotlin.math.PI ) }
视图定义组件
视图定义由描述视图功能和行为的 DSL 组件组成。这些组件只能在 View 闭包中使用。
名称
设置 JavaScript 代码将用于引用视图的视图名称。接受一个字符串作为参数。这可以从视图的类名推导出来,但为了清晰起见建议显式设置。
Name("MyViewName")
Prop
定义给定名称的视图属性的 setter。
参数
- name:
String— 您希望定义 setter 的视图属性的名称。 - defaultValue:
ValueType— 当 setter 被调用时使用的可选默认值,值为null。 - setter:
(view: ViewType, value: ValueType) -> ()— 在视图重新渲染时调用的闭包。
此属性只能在 View 闭包中使用。
Prop("background") { (view: UIView, color: UIColor) in view.backgroundColor = color }
Prop("background") { view: View, @ColorInt color: Int -> view.setBackgroundColor(color) }
具有默认值的属性定义。
Prop("background", UIColor.black) { (view: UIView, color: UIColor) in view.backgroundColor = color }
Prop("background", Color.BLACK) { view: View, @ColorInt color: Int -> view.setBackgroundColor(color) }
注意: 目前不支持函数类型(回调)的属性。
OnViewDidUpdateProps
定义视图生命周期方法,当视图完成更新所有属性时调用。
View(MyView.self) { OnViewDidUpdateProps { view: MyView in %%placeholder-start%%... %%placeholder-end%% } }
View(MyView::class) { OnViewDidUpdateProps { view: MyView -> %%placeholder-start%%... %%placeholder-end%% } }
(View) AsyncFunction
与模块定义中的 AsyncFunction 类似,您可以定义附加到视图引用的函数,以允许直接修改本机视图。
视图异步函数将始终在主队列上调度,并可以将视图实例作为第一个参数接收。
View(MyView.self) { AsyncFunction("myAsyncFunction") { (view: MyView, message: String) in view.displayMessage(message) } }
View(MyView::class) { AsyncFunction("myAsyncFunction") { view: MyView, message: String -> view.displayMessage(message); } }
const MyNativeView = requireNativeViewManager('MyView'); function MyComponent() { const ref = React.useRef(null); React.useEffect(() => { ref.current?.myAsyncFunction(); }, [ref]); return <MyNativeView ref={ref} />; }
GroupView
使视图能够作为视图组使用。可以在组视图定义中接受的定义组件:AddChildView, GetChildCount, GetChildViewAt, RemoveChildView, RemoveChildViewAt。
参数
- viewType — 本机视图的类。注意,提供的类必须继承自 Android
ViewGroup。 - definition:
() -> ViewGroupDefinition— 视图组定义的构建器。
此属性只能在 View 闭包中使用。
GroupView<ViewGroup> { AddChildView { parent, child, index -> %%placeholder-start%%... %%placeholder-end%%} }
AddChildView
定义将子视图添加到视图组的动作。
参数
- action:
(parent: ParentType, child: ChildType, index: Int) -> ()— 将子视图添加到视图组的动作。
此属性只能在 GroupView 闭包中使用。
AddChildView { parent, child: View, index -> parent.addView(child, index) }
GetChildCount
定义获取视图组中子视图数量的动作。
参数
- action:
(parent: ParentType) -> Int— 返回子视图数量的函数。
此属性只能在 GroupView 闭包中使用。
GetChildCount { parent -> return@GetChildCount parent.childCount }
GetChildViewAt
定义从视图组中检索特定索引的子视图的动作。
参数
- action:
(parent: ParentType, index: Int) -> ChildType— 从视图组检索特定索引的子视图的函数。
此属性只能在 GroupView 闭包中使用。
GetChildViewAt { parent, index -> parent.getChildAt(index) }
RemoveChildView
定义从视图组中移除特定子视图的动作。
参数
- action:
(parent: ParentType, child: ChildType) -> ()— 从视图组中移除特定子视图的函数。
此属性只能在 GroupView 闭包中使用。
RemoveChildView { parent, child: View -> parent.removeView(child) }
RemoveChildViewAt
定义从视图组中移除特定索引的子视图的动作。
参数
- action:
(parent: ParentType, child: ChildType) -> ()— 从视图组中移除在特定索引的子视图的函数。
此属性只能在 GroupView 闭包中使用。
RemoveChildViewAt { parent, index -> parent.removeViewAt(child) }
参数类型
从根本上说,只有原始类型和可序列化数据可以在线程之间来回传递。然而,通常原生模块需要接收自定义数据结构——比只带值为未知(Any)类型的字典/映射更复杂,因此每个值必须单独验证和转换。Expo 模块 API 提供协议,使得处理数据对象更为方便,提供自动验证,最终确保每个对象成员的本机类型安全。
原始类型
所有函数和视图属性 setter 接受所有 Swift 和 Kotlin 中的公共原始类型作为参数。这包括原始类型的数组、字典/映射和可选值。
| 语言 | 支持的原始类型 |
|---|---|
| Swift | Bool, Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, Float32, Double, String |
| Kotlin | Boolean, Int, Long, Float, Double, String, Pair |
可转换类型
可转换类型 是指可以从某些特定类型的数据(从 JavaScript 接收)初始化的本机类型。这种类型可以作为 Function 主体中的参数类型使用。例如,当 CGPoint 类型作为函数参数类型使用时,其实例可以从一个包含两个数字的数组 (x, y) 或一个具有数值 x 和 y 属性的 JavaScript 对象创建。
内置的可转换类型在 后面 中有更详细的文档。您可以通过使本机 Swift 类型符合 Convertible 协议来定义额外的可转换类型:
Convertible
Convertible 是一个 Swift 协议,具有一个静态方法:
| Parameter | Type | Description |
|---|---|---|
| value | Any? | 要转换的来自 JavaScript 的值 |
| appContext | AppContext | 当前运行 Expo 应用实例的上下文对象 |
一个将动态类型的值从 JavaScript 转换为符合 Convertible 的 Swift 类型实例的静态方法。实现者应在给定值无效或类型不受支持时抛出异常。
Self示例
import ExpoModulesCore extension CMTime: @retroactive Convertible { public static func convert(from value: Any?, appContext: AppContext) throws -> CMTime { if let seconds = value as? Double { return CMTime(seconds: seconds, preferredTimescale: .max) } throw Conversions.ConvertingException<CMTime>(value) } }
信息 支持使用 Kotlin 定义可转换类型计划将在 SDK 53 可用。
内置可转换类型
一些来自 CoreGraphics 和 UIKit 系统框架的常见 iOS 类型已经被设置为可转换。
| 原生 iOS 类型 | TypeScript |
|---|---|
URL | string 具有 URL。当未提供方案时,假设为文件 URL。 |
CGFloat | number |
CGPoint | { x: number, y: number } 或 number[] 具有 x 和 y 坐标 |
CGSize | { width: number, height: number } 或 number[] 具有 width 和 height |
CGVector | { dx: number, dy: number } 或 number[] 具有 dx 和 dy 向量差异 |
CGRect | { x: number, y: number, width: number, height: number } 或 number[] 具有 x, y, width 和 height 值 |
CGColorUIColor | 颜色十六进制字符串(#RRGGBB, #RRGGBBAA, #RGB, #RGBA),遵循 CSS3/SVG 规范 的命名颜色或 "transparent" |
Data | Uint8Array SDK 50+ |
同样,一些来自 java.io、java.net 或 android.graphics 等包的常见 Android 类型也被设置为可转换。
注意: 在 Android 上,尽可能使用原始数组。
| 原生 Android 类型 | TypeScript |
|---|---|
java.net.URL | string 具有 URL。请注意,必须提供方案(URL 不应包含任何未编码的 % 字符) |
android.net.Urijava.net.URI | string 具有 URI。请注意,必须提供方案(URI 不应包含任何未编码的 % 字符) |
java.io.Filejava.nio.file.Path(仅在 Android API 26 上可用) | string 具有文件路径 |
android.graphics.Color | 颜色十六进制字符串(#RRGGBB, #RRGGBBAA, #RGB, #RGBA),遵循 CSS3/SVG 规范 的命名颜色或 "transparent" |
kotlin.Pair<A, B> | 包含两个值的数组,第一个值为 A 类型,第二个值为 B 类型 |
kotlin.ByteArray | Uint8Array SDK 50+ |
kotlin.BooleanArray | boolean[] |
kotlin.IntArraykotlin.FloatArraykotlin.LongArraykotlin.DoubleArray | number[] |
kotlin.time.Duration | number 表示持续时间(秒) SDK 52+ |
记录
记录 是可转换类型,是字典(Swift)或映射(Kotlin)的等价物,但表示为一个结构体,每个字段可以具有其类型并提供默认值。
它是表示具有本机类型安全的 JavaScript 对象的更好方式。
struct FileReadOptions: Record { @Field var encoding: String = "utf8" @Field var position: Int = 0 @Field var length: Int? } // 现在该记录可以用作函数或视图属性 setter 的参数。 Function("readFile") { (path: String, options: FileReadOptions) -> String in // 使用给定的 `options` 读取文件 }
class FileReadOptions : Record { @Field val encoding: String = "utf8" @Field val position: Int = 0 @Field val length: Int? = null } // 现在该记录可以用作函数或视图属性 setter 的参数。 Function("readFile") { path: String, options: FileReadOptions -> // 使用给定的 `options` 读取文件 }
枚举
使用枚举,我们可以更进一步,限制上述示例(使用 FileReadOptions 记录)支持的编码为 "utf8" 和 "base64"。要使用枚举作为参数或记录字段,它必须表示原始值(例如,String,Int),并遵循 Enumerable。
enum FileEncoding: String, Enumerable { case utf8 case base64 } struct FileReadOptions: Record { @Field var encoding: FileEncoding = .utf8 %%placeholder-start%%... %%placeholder-end%% }
// 注意:构造函数必须带有一个名为 value 的参数。 enum class FileEncoding(val value: String) : Enumerable { utf8("utf8"), base64("base64") } class FileReadOptions : Record { @Field val encoding: FileEncoding = FileEncoding.utf8 %%placeholder-start%%... %%placeholder-end%% }
双重类型
有一些用例需要将不同的类型传递给单个函数参数。这时,双重类型可能会有用。 它们作为某几种类型中的一种的值的容器。
Function("foo") { (bar: Either<String, Int>) in if let bar: String = bar.get() { // `bar` 是一个字符串 } if let bar: Int = bar.get() { // `bar` 是一个整数 } }
Function("foo") { bar: Either<String, Int> -> bar.get(String::class).let { // `it` 是一个字符串 } bar.get(Int::class).let { // `it` 是一个整数 } }
目前提供三种双重类型的实现,允许您使用多达四种不同的子类型。
Either<FirstType, SecondType>— 两种类型之一的容器。EitherOfThree<FirstType, SecondType, ThirdType>— 三种类型之一的容器。EitherOfFour<FirstType, SecondType, ThirdType, FourthType>— 四种类型之一的容器。
JavaScript 值
也可以使用 JavaScriptValue 类型,这是一个可以表示于 JavaScript 中的任何值的持有者。
当您要修改给定参数或想要省略类型验证和转换时,此类型非常有用。
请注意,使用特定于 JavaScript 的类型限制在同步函数中,因为 JavaScript 运行时中的所有读取和写入必须在线程中进行。
从不同线程访问这些值将导致崩溃。
除了原始值外,可以使用 JavaScriptObject 类型仅允许对象类型,JavaScriptFunction<ReturnType> 用于回调。
Function("mutateMe") { (value: JavaScriptValue) in if value.isObject() { let jsObject = value.getObject() jsObject.setProperty("expo", value: "modules") } } // 或 Function("mutateMe") { (jsObject: JavaScriptObject) in jsObject.setProperty("expo", value: "modules") }
Function("mutateMe") { value: JavaScriptValue -> if (value.isObject()) { val jsObject = value.getObject() jsObject.setProperty("expo", "modules") } } // 或 Function("mutateMe") { jsObject: JavaScriptObject -> jsObject.setProperty("expo", "modules") }
本机类
模块
原生模块的基类。
属性
提供对 AppContext 的访问。
AppContext方法
| Parameter | Type | Description |
|---|---|---|
| eventName | string | JavaScript 事件的名称 |
| payload | Android: Map<String, Any?> | Bundle
iOS: [String: Any?] | 事件有效负载 |
使用给定名称和有效负载将事件发送到 JavaScript。请参见 发送事件
voidExpoView
所有导出视图应使用的基类。
在 iOS 上,ExpoView 扩展了处理一些样式(例如边框)和可访问性的 RCTView。
属性
提供对 AppContext 的访问。
AppContext扩展 ExpoView
要使用 View 组件导出您的视图,您的自定义类必须继承自 ExpoView。通过这样做,您将能够访问 AppContext 对象。这是与其他模块和 JavaScript 运行时通信的唯一方法。此外,您不能更改构造函数参数,因为提供的视图将由 expo-modules-core 初始化。
class LinearGradientView: ExpoView {} public class LinearGradientModule: Module { public func definition() -> ModuleDefinition { View(LinearGradientView.self) { %%placeholder-start%%... %%placeholder-end%% } } }
class LinearGradientView( context: Context, appContext: AppContext, ) : ExpoView(context, appContext) class LinearGradientModule : Module() { override fun definition() = ModuleDefinition { View(LinearGradientView::class) { %%placeholder-start%%... %%placeholder-end%% } } }
指南
发送事件
虽然 JavaScript/TypeScript 到本机的通信主要由本机函数覆盖,但您可能还想让 JavaScript/TypeScript 代码知道某些系统事件,例如剪贴板内容的更改。
为此,在模块定义中,您需要提供模块可以使用 Events 定义组件发送的事件名称。之后,您可以在模块实例上使用 sendEvent(eventName, payload) 函数来发送实际事件及其有效负载。例如,最小的剪贴板实现可以发送本机事件如下所示:
let CLIPBOARD_CHANGED_EVENT_NAME = "onClipboardChanged" public class ClipboardModule: Module { public func definition() -> ModuleDefinition { Events(CLIPBOARD_CHANGED_EVENT_NAME) OnStartObserving { NotificationCenter.default.addObserver( self, selector: #selector(self.clipboardChangedListener), name: UIPasteboard.changedNotification, object: nil ) } OnStopObserving { NotificationCenter.default.removeObserver( self, name: UIPasteboard.changedNotification, object: nil ) } } @objc private func clipboardChangedListener() { sendEvent(CLIPBOARD_CHANGED_EVENT_NAME, [ "contentTypes": availableContentTypes() ]) } }
const val CLIPBOARD_CHANGED_EVENT_NAME = "onClipboardChanged" class ClipboardModule : Module() { override fun definition() = ModuleDefinition { Events(CLIPBOARD_CHANGED_EVENT_NAME) OnStartObserving { clipboardManager?.addPrimaryClipChangedListener(listener) } OnStopObserving { clipboardManager?.removePrimaryClipChangedListener(listener) } } private val clipboardManager: ClipboardManager? get() = appContext.reactContext?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager private val listener = ClipboardManager.OnPrimaryClipChangedListener { clipboardManager?.primaryClipDescription?.let { clip -> this@ClipboardModule.sendEvent( CLIPBOARD_CHANGED_EVENT_NAME, bundleOf( "contentTypes" to availableContentTypes(clip) ) ) } } }
要在 JavaScript/TypeScript 中订阅这些事件,请使用 addListener 对由 requireNativeModule 返回的模块对象进行处理。模块扩展了内置的 EventEmitter 类。
或者,您可以使用 useEvent 或 useEventListener 钩子。
import { requireNativeModule, NativeModule } from 'expo'; type ClipboardChangeEvent = { contentTypes: string[]; }; type ClipboardModuleEvents = { onClipboardChanged(event: ClipboardChangeEvent): void; }; declare class ClipboardModule extends NativeModule<ClipboardModuleEvents> {} const Clipboard = requireNativeModule<ClipboardModule>('Clipboard'); Clipboard.addListener('onClipboardChanged', (event: ClipboardChangeEvent) => { alert('Clipboard has changed'); });
视图回调
某些事件与特定视图相关联。例如,触摸事件只应发送到被按下的底层 JavaScript 视图。在这种情况下,您不能使用 发送事件 中描述的 sendEvent。expo-modules-core 引入了一种视图回调机制以处理视图绑定事件。
要使用它,在视图定义中,您需要使用 Events 定义组件提供视图可以发送的事件名称。之后,您需要在视图类中声明一个类型为 EventDispatcher 的属性。声明的属性名称必须与 Events 组件中导出的名称相同。然后,您可以将其作为函数调用,并传递类型为 [String: Any?] 的有效负载(在 iOS 上为 Map<String, Any?>。
注意: 在 Android 上,可以指定有效负载类型。在类型无法转换为对象的情况下,有效负载将被封装并存储在
payload键下:{payload: <provided value>}。
class CameraViewModule: Module { public func definition() -> ModuleDefinition { View(CameraView.self) { Events( "onCameraReady" ) %%placeholder-start%%... %%placeholder-end%% } } } class CameraView: ExpoView { let onCameraReady = EventDispatcher() func callOnCameraReady() { onCameraReady([ "message": "Camera was mounted" ]); } }
class CameraViewModule : Module() { override fun definition() = ModuleDefinition { View(ExpoCameraView::class) { Events( "onCameraReady" ) %%placeholder-start%%... %%placeholder-end%% } } } class CameraView( context: Context, appContext: AppContext ) : ExpoView(context, appContext) { val onCameraReady by EventDispatcher() fun callOnCameraReady() { onCameraReady(mapOf( "message" to "Camera was mounted" )); } }
要在 JavaScript/TypeScript 中订阅这些事件,您需要将函数传递给本机视图,如下所示:
import { requireNativeViewManager } from 'expo-modules-core'; const CameraView = requireNativeViewManager('CameraView'); export default function MainView() { const onCameraReady = event => { console.log(event.nativeEvent); }; return <CameraView onCameraReady={onCameraReady} />; }
提供的有效负载可在 nativeEvent 键下使用。
示例
public class MyModule: Module { public func definition() -> ModuleDefinition { Name("MyFirstExpoModule") Function("hello") { (name: String) in return "Hello \(name)!" } } }
class MyModule : Module() { override fun definition() = ModuleDefinition { Name("MyFirstExpoModule") Function("hello") { name: String -> return "Hello $name!" } } }
要了解来自真实模块的更多示例,可以参考在 GitHub 上使用此 API 的 Expo 模块: