Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cndocs/alertios.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
id: alertios
title: AlertIOS
title: '❌ AlertIOS'
---

:::danger 已从 React Native 中移除
Expand Down
4 changes: 2 additions & 2 deletions cndocs/asyncstorage.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
id: asyncstorage
title: 🚧 AsyncStorage
title: '❌ AsyncStorage'
---

:::危险已从 React Native 中移除
:::danger 已从 React Native 中移除
请改用[社区包](https://reactnative.directory/?search=storage)之一。
:::
84 changes: 42 additions & 42 deletions cndocs/fabric-native-components-android.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
id: fabric-native-components-android
title: 'Fabric 原生 UI 组件:Android'
title: 'Fabric 原生组件:Android'
---

import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import constants from '@site/core/TabsConstants';
Expand Down Expand Up @@ -163,9 +163,9 @@ class ReactWebView: WebView {
eventDispatcher?.dispatchEvent(event)
}

enum class OnScriptLoadedEventResult() {
success(),
error()
enum class OnScriptLoadedEventResult {
success,
error;
}

inner class OnScriptLoadedEvent(
Expand All @@ -190,24 +190,24 @@ class ReactWebView: WebView {
然后,代码定义了一个实际发出事件的帮助函数。要发出事件,你必须:

- 获取 `ReactContext` 的引用;
- retrieve the `surfaceId` of the view that you are presenting;
- grab a reference to the `eventDispatcher` associated with the view;
- build the payload for the event using a `WritableMap` object;
- create the event object that you need to send to JavaScript;
- call the `eventDispatcher.dispatchEvent` to send the event.
- 获取当前所呈现视图的 `surfaceId`
- 获取与该视图关联的 `eventDispatcher` 引用;
- 使用 `WritableMap` 对象构建事件的负载;
- 创建需要发送给 JavaScript 的事件对象;
- 调用 `eventDispatcher.dispatchEvent` 发送事件。

The last part of the file contains the definition of the data types you need to send the event:
文件的最后一部分包含了发送事件所需的数据类型定义:

- The `OnScriptLoadedEventResult`, with the possible outcomes of the `OnScriptLoaded` event.
- The actual ``OnScriptLoadedEvent` that needs to extend the React Native's `Event` class.
- `OnScriptLoadedEventResult`,表示 `OnScriptLoaded` 事件可能的结果。
- 实际的 `OnScriptLoadedEvent`,它需要继承 React Native`Event` 类。

### 3. Write the `WebViewManager`
### 3. 编写 `WebViewManager`

The `WebViewManager` is the class that connects the React Native runtime with the native view.
`WebViewManager` 是将 React Native 运行时与原生视图连接起来的类。

When React receives the instruction from the app to render a specific component, React uses the registered view manager to create the view and to pass all the required properties.
React 收到应用发来的渲染某个组件的指令时,它会使用已注册的 view manager 来创建该视图,并传入所有必需的属性。

This is the code of the `ReactWebViewManager`.
下面是 `ReactWebViewManager` 的代码。

<Tabs groupId="android-language" queryString defaultValue={constants.defaultAndroidLanguage} values={constants.androidLanguages}>
<TabItem value="java">
Expand Down Expand Up @@ -327,31 +327,31 @@ class ReactWebViewManager(context: ReactApplicationContext) : SimpleViewManager<
</TabItem>
</Tabs>

The `ReactWebViewManager` extends the `SimpleViewManager` class from React and implements the `CustomWebViewManagerInterface`, generated by Codegen.
`ReactWebViewManager` 继承了 React 的 `SimpleViewManager` 类,并实现了由 Codegen 生成的 `CustomWebViewManagerInterface`

It holds a reference of the `CustomWebViewManagerDelegate`, another element generated by Codegen.
它持有 `CustomWebViewManagerDelegate` 的引用,这也是由 Codegen 生成的另一个对象。

It then overrides the `getName` function, which must return the same name used in the spec's `codegenNativeComponent` function call.
随后它重写了 `getName` 函数,该函数必须返回与 spec`codegenNativeComponent` 调用所使用名称相同的值。

The `createViewInstance` function is responsible to instantiate a new `ReactWebView`.
`createViewInstance` 函数负责实例化一个新的 `ReactWebView`

Then, the ViewManager needs to define how all the React's compnoents props will update the native view. In the example, you need to decide how to handle the `sourceURL` property that React will set on the `WebView`.
接着,ViewManager 需要定义 React 组件的各个 props 如何更新原生视图。在这个示例中,你需要决定如何处理 React 设置到 `WebView` 上的 `sourceURL` 属性。

Finally, if the component can emit an event, you need to map the event name by overriding the `getExportedCustomBubblingEventTypeConstants` for bubbling events, or the `getExportedCustomDirectEventTypeConstants` for direct events.
最后,如果组件可以发出事件,你需要通过重写冒泡事件对应的 `getExportedCustomBubblingEventTypeConstants`,或直接事件对应的 `getExportedCustomDirectEventTypeConstants`,来映射事件名称。

### 4. Write the `ReactWebViewPackage`
### 4. 编写 `ReactWebViewPackage`

As you do with Native Modules, Native Components also need to implement the `ReactPackage` class. This is an object that you can use to register the component in the React Native runtime.
Native Modules 类似,Native Components 也需要实现 `ReactPackage` 类。你可以使用这个对象在 React Native 运行时中注册组件。

This is the code for the `ReactWebViewPackage`:
下面是 `ReactWebViewPackage` 的代码:

<Tabs groupId="android-language" queryString defaultValue={constants.defaultAndroidLanguage} values={constants.androidLanguages}>
<TabItem value="java">

```java title="Demo/android/src/main/java/com/webview/ReactWebViewPackage.java"
package com.webview;

import com.facebook.react.TurboReactPackage;
import com.facebook.react.BaseReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.model.ReactModuleInfo;
Expand All @@ -363,7 +363,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ReactWebViewPackage extends TurboReactPackage {
public class ReactWebViewPackage extends BaseReactPackage {
@Override
public List<ViewManager<?, ?>> createViewManagers(ReactApplicationContext reactContext) {
return Collections.singletonList(new ReactWebViewManager(reactContext));
Expand All @@ -381,7 +381,7 @@ public class ReactWebViewPackage extends TurboReactPackage {
public ReactModuleInfoProvider getReactModuleInfoProvider() {
return new ReactModuleInfoProvider() {
@Override
public Map<String, ReactModuleInfo> get() {
public Map<String, ReactModuleInfo> getReactModuleInfos() {
Map<String, ReactModuleInfo> map = new HashMap<>();
map.put(ReactWebViewManager.REACT_CLASS, new ReactModuleInfo(
ReactWebViewManager.REACT_CLASS, // name
Expand All @@ -401,17 +401,17 @@ public class ReactWebViewPackage extends TurboReactPackage {
</TabItem>
<TabItem value="kotlin">

```kotlin title="Demo/android/src/main/java/com/webview/ReactWebView.kt"
```kotlin title="Demo/android/src/main/java/com/webview/ReactWebViewPackage.kt"
package com.webview

import com.facebook.react.TurboReactPackage
import com.facebook.react.BaseReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.model.ReactModuleInfo
import com.facebook.react.module.model.ReactModuleInfoProvider
import com.facebook.react.uimanager.ViewManager

class ReactWebViewPackage : TurboReactPackage() {
class ReactWebViewPackage : BaseReactPackage() {
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return listOf(ReactWebViewManager(reactContext))
}
Expand All @@ -425,10 +425,10 @@ class ReactWebViewPackage : TurboReactPackage() {

override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider {
mapOf(ReactWebViewManager.REACT_CLASS to ReactModuleInfo(
_name = ReactWebViewManager.REACT_CLASS,
_className = ReactWebViewManager.REACT_CLASS,
_canOverrideExistingModule = false,
_needsEagerInit = false,
name = ReactWebViewManager.REACT_CLASS,
className = ReactWebViewManager.REACT_CLASS,
canOverrideExistingModule = false,
needsEagerInit = false,
isCxxModule = false,
isTurboModule = true,
)
Expand All @@ -440,15 +440,15 @@ class ReactWebViewPackage : TurboReactPackage() {
</TabItem>
</Tabs>

The `ReactWebViewPackage` extends the `TurboReactPackage` and implements all the methods required to properly register our component.
`ReactWebViewPackage` 继承了 `BaseReactPackage`,并实现了正确注册组件所需的全部方法。

- the `createViewManagers` method is the factory method that creates the `ViewManager` that manage the custom views.
- the `getModule` method returns the proper ViewManager depending on the View that React Native needs to render.
- the `getReactModuleInfoProvider` provides all the information required when registering the module in the runtime,
- `createViewManagers` 方法是一个工厂方法,用于创建管理自定义视图的 `ViewManager`
- `getModule` 方法会根据 React Native 需要渲染的 View 返回相应的 ViewManager。
- `getReactModuleInfoProvider` 提供了在运行时注册模块时所需的全部信息。

### 5. Register the `ReactWebViewPackage` in the application
### 5. 在应用中注册 `ReactWebViewPackage`

Finally, you need to register the `ReactWebViewPackage` in the application. We do that by modifying the `MainApplication` file by adding the `ReactWebViewPackage` to the list of packages returned by the `getPackages` function.
最后,你需要在应用中注册 `ReactWebViewPackage`。具体做法是修改 `MainApplication` 文件,把 `ReactWebViewPackage` 添加到 `getPackages` 函数返回的包列表中。

```kotlin title="Demo/app/src/main/java/com/demo/MainApplication.kt"
package com.demo
Expand Down Expand Up @@ -497,4 +497,4 @@ class MainApplication : Application(), ReactApplication {
}
}

```
```
65 changes: 34 additions & 31 deletions cndocs/gesture-responder-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,64 @@ id: gesture-responder-system
title: 手势响应系统
---

移动设备上的手势识别要比在 web 上复杂得多。用户的一次触摸操作的真实意图是什么,App 要经过好几个阶段才能判断。比如 App 需要判断用户的触摸到底是在滚动页面,还是滑动一个 widget,或者只是一个单纯的点击。甚至随着持续时间的不同,这些操作还会转化。此外,还有多点同时触控的情况
手势响应系统用于管理应用中手势的生命周期。一次触摸会经历多个阶段,应用会在这个过程中判断用户的意图。例如,应用需要判断这次触摸是在滚动、在某个组件上滑动,还是在点击。而且这种判断在触摸持续期间还可能发生变化。此外,还可能存在多个同时发生的触摸

手势响应系统可以使组件在不关心父组件或子组件的前提下自行处理触摸交互
触摸响应系统的作用,是让组件在不额外了解其父组件或子组件的前提下,协商如何处理这些触摸交互

### 最佳实践

用户之所以会觉得 web app 和原生 app 在体验上有巨大的差异,触摸响应是一大关键因素。用户的每一个操作都应该具有下列属性
为了让你的应用拥有更好的体验,每一次交互都应具备以下特性

反馈/高亮 —— 让用户看到他们到底按到了什么东西,以及松开手后会发生什么。取消功能 —— 当用户正在触摸操作时,应该是可以通过把手指移开来终止操作。这些特性使得用户在使用 App 时体验更好,因为它们可以让用户大胆试用,而不必担心点错了什么。
- 反馈 / 高亮 —— 告诉用户当前是哪个元素在响应他们的触摸,以及松开手指后会发生什么。
- 可取消性 —— 当用户正在执行某个操作时,应该能够通过将手指拖离来中止它。

这些特性会让用户在使用应用时更加安心,因为它们允许人们大胆尝试和交互,而不必担心误操作。

### TouchableHighlight 与 Touchable 系列组件

响应系统用起来可能比较复杂。所以我们提供了一个抽象的`Touchable`实现,用来做“可触控”的组件。这一实现利用了响应系统,使得你可以简单地以声明的方式来配置触控处理。如果要做一个按钮或者网页链接,那么使用`TouchableHighlight`就可以
响应系统用起来可能比较复杂。因此,我们为那些需要“可点按”行为的场景提供了一个抽象的 `Touchable` 实现。它基于响应系统,并允许你以声明式的方式配置点击交互。在任何你会在 Web 上使用按钮或链接的地方,都可以使用 `TouchableHighlight`。

## 响应者的生命周期

一个 View 只要实现了正确的协商方法,就可以成为触摸事件的响应者。我们通过两个方法去“询问”一个 View 是否愿意成为响应者:

* `View.props.onStartShouldSetResponder: (evt) => true,` - 在用户开始触摸的时候(手指刚刚接触屏幕的瞬间),是否愿意成为响应者
* `View.props.onMoveShouldSetResponder: (evt) => true,` - 如果 View 不是响应者,那么在每一个触摸点开始移动(没有停下也没有离开屏幕)时再询问一次:是否愿意响应触摸交互呢
- `View.props.onStartShouldSetResponder: evt => true,` - 这个 View 是否希望在触摸开始时成为响应者
- `View.props.onMoveShouldSetResponder: evt => true,` - 当这个 View 还不是响应者时,每次触摸在其上移动都会调用此函数:这个 View 是否想要“接管”触摸响应

如果 View 返回 true,并开始尝试成为响应者,那么会触发下列事件之一:
如果 View 返回 true 并尝试成为响应者,则会发生以下情况之一:

* `View.props.onResponderGrant: (evt) => {}` - View 现在要开始响应触摸事件了。这也是需要做高亮的时候,使用户知道他到底点到了哪里
* `View.props.onResponderReject: (evt) => {}` - 响应者现在“另有其人”而且暂时不会“放权”,请另作安排
- `View.props.onResponderGrant: evt => {}` - 这个 View 现在开始响应触摸事件了。此时通常应该高亮界面,让用户知道发生了什么
- `View.props.onResponderReject: evt => {}` - 当前已有别的响应者,并且它不会释放响应权

如果 View 已经开始响应触摸事件了,那么下列这些处理函数会被一一调用
如果 View 正在响应触摸,则可能会调用以下处理函数

* `View.props.onResponderMove: (evt) => {}` - 用户正在屏幕上移动手指时(没有停下也没有离开屏幕)
* `View.props.onResponderRelease: (evt) => {}` - 触摸操作结束时触发,比如"touchUp"(手指抬起离开屏幕)
* `View.props.onResponderTerminationRequest: (evt) => true` - 有其他组件请求接替响应者,当前的 View 是否“放权”?返回 true 的话则释放响应者权力
* `View.props.onResponderTerminate: (evt) => {}` - 响应者权力已经交出。这可能是由于其他 View 通过`onResponderTerminationRequest`请求的,也可能是由操作系统强制夺权(比如 iOS 上的控制中心或是通知中心)。
- `View.props.onResponderMove: evt => {}` - 用户正在移动手指
- `View.props.onResponderRelease: evt => {}` - 在触摸结束时触发,也就是“touchUp
- `View.props.onResponderTerminationRequest: evt => true` - 有别的元素想要成为响应者。这个 View 是否应该释放响应权?返回 true 表示允许释放
- `View.props.onResponderTerminate: evt => {}` - 响应权已从该 View 转移走。可能是在调用 `onResponderTerminationRequest` 之后被其他 View 接管,也可能是在未询问的情况下被操作系统接管(例如 iOS 上打开控制中心或通知中心时)。

`evt`是一个合成事件,它包含以下结构
`evt` 是一个合成触摸事件,结构如下

* `nativeEvent`
* `changedTouches` - 在上一次事件之后,所有发生变化的触摸事件的数组集合(即上一次事件后,所有移动过的触摸点)
* `identifier` - 触摸点的 ID
* `locationX` - 触摸点相对于当前元素的横坐标
* `locationY` - 触摸点相对于当前元素的纵坐标
* `pageX` - 触摸点相对于根元素的横坐标
* `pageY` - 触摸点相对于根元素的纵坐标
* `target` - 触摸点所在的元素 ID
* `timestamp` - 触摸事件的时间戳,可用于移动速度的计算
* `touches` - 当前屏幕上的所有触摸点的集合
- `nativeEvent`
- `changedTouches` - 自上一次事件以来发生变化的所有触摸事件数组
- `identifier` - 触摸点的 ID
- `locationX` - 触摸点相对于当前元素的 X 坐标
- `locationY` - 触摸点相对于当前元素的 Y 坐标
- `pageX` - 触摸点相对于根元素的 X 坐标
- `pageY` - 触摸点相对于根元素的 Y 坐标
- `target` - 接收触摸事件的元素节点 ID
- `timestamp` - 触摸时间标识,可用于速度计算
- `touches` - 当前屏幕上的所有触摸点数组

### 捕获 ShouldSet 事件处理

`onStartShouldSetResponder``onMoveShouldSetResponder`是以冒泡的形式调用的,即嵌套最深的节点最先调用。这意味着当多个 View 同时在`*ShouldSetResponder`中返回 true 时,最底层的 View 将优先“夺权”。在多数情况下这并没有什么问题,因为这样可以确保所有控件和按钮是可用的
`onStartShouldSetResponder``onMoveShouldSetResponder` 采用冒泡顺序调用,最深层的节点会最先收到调用。这意味着当多个 View `*ShouldSetResponder` 处理函数中都返回 true 时,最深层的组件会成为响应者。在大多数场景下,这正是我们想要的效果,因为它能确保所有控件和按钮都可用

但是有些时候,某个父 View 会希望能先成为响应者。我们可以利用“捕获期”来解决这一需求。响应系统在从最底层的组件开始冒泡之前,会首先执行一个“捕获期”,在此期间会触发`on*ShouldSetResponderCapture`系列事件。因此,如果某个父 View 想要在触摸操作开始时阻止子组件成为响应者,那就应该处理`onStartShouldSetResponderCapture`事件并返回 true 值
但有时父级组件会希望确保自己成为响应者。这时可以使用捕获阶段。在响应系统从最深层组件开始冒泡之前,会先执行一次捕获阶段,并触发 `on*ShouldSetResponderCapture`。因此,如果父级 View 想在触摸开始时阻止子组件成为响应者,它就应该在 `onStartShouldSetResponderCapture` 处理函数中返回 true

* `View.props.onStartShouldSetResponderCapture: (evt) => true,`
* `View.props.onMoveShouldSetResponderCapture: (evt) => true,`
- `View.props.onStartShouldSetResponderCapture: evt => true,`
- `View.props.onMoveShouldSetResponderCapture: evt => true,`

### PanResponder

要使用更高级的手势功能,请参看[PanResponder](panresponder.md).
如果你需要更高层级的手势解释能力,请参阅 [PanResponder](panresponder.md)
Loading