# 与UI交互

## 介绍

数据最终由组件显示在屏幕上之前会在整个 Section 层级结构中流动。 Sections 提供一系列功能，让你可以对数据变化做出响应以及同UI进行交互。

## @OnRefresh

当由 Section 层级结构渲染的 UI 需要对内容进行刷新时，带有 @OnRefresh 注解的方法就会被调用。

### API

调用 SectionTree#refresh() 方法将刷新请求传递给层级结构中的所有 Sections 。在 Section 中可以这样处理：

```
class SectionSpec {

  @OnRefresh
  static void onRefresh(SectionContext c, @Prop yourProp, @State yourState) {
    // Handle your refresh request
  }
}
```

## @OnViewportChanged

当可见窗口中发生变化时会调用带有 @OnViewportChanged 的方法。

### API

调用 SectionTree#viewPortChanged() 和 SectionTree#viewPortChangedFromScrolling() 方法可以让 Sections 知道当前窗口发生了变化。

```
class SectionSpec {

  @OnViewportChanged
  static void onViewportChanged(
    SectionContext c,
    int firstVisiblePosition,
    int lastVisiblePosition,
    int totalCount,
    int firstFullyVisibleIndex,
    int lastFullyVisibleIndex,
    @Prop YourProp prop,
    @State YourState state) {
  }
}
```

1\) firstVisiblePosition 窗口中第一个可见组件的位置，组件在窗口中部分被隐藏。

2\) lastVisiblePosition 窗口中最后一个可见组件的位置，组件在窗口中部分被隐藏。

3\) totalCount Section 层级结构中所有条目的总数，这个 Section 是指包含注解方法的那个根节点 Section 。

4\) firstFullyVisibleIndex 窗口中第一个完全可组件的位置。

5\) lastFullyVisibleIndex 窗口中最后一个完全可见组件的位置。

### 窗口中的变化

窗口中给的变化可以由下列的这些原因引发的：

1\) 添加新的组件到窗口中

2\) 从窗口中删除组件

3）滚动列表

4\) 窗口中的组件被更新

5\) 组件被移入或者移出可见窗口

### 位置与数量

onViewportChanged 方法中返回的位置与数量是相对于该 section 所含有的组件数而言的。例如：

```
       Section A
      /         \
 Section B   Section C
    |           |
  10 items    10 items
  (visible)   (hidden)
```

当 Section C 的第一个组件因为滑动进入窗口中，Section C 的 firstVisiblePosition 值就是 0 ，同时 Section B 的 lastVisiblePosition 的值就是 10 。

## @OnDataBound

当此 Section 的层级结构中的数据变化对 SectionTree.Target 可见时，带有这个注解的方法会被调用。

数据变化有这几个原因： 1） 插入 2) 更新 3） 删除 4） 移动

数据发生变化并不意味着这些数据在窗口中变得可见。为了检测可见性，使用 带有 @OnViewportChanged 注解的方法。

```
class SectionSpec {

  @OnDataBound
  static void OnDataBound(
    SectionContext c,
    @Prop YourProp prop
    @State YourState state) {
    // Handle data changes
  }
}
```

## requestFocus()

调用这个方法可以时 Section 中的组件获得焦点。如果一个组件在窗口中被隐藏，section 会被滚动以显示这个组件。

在请求获取焦点之前，用于渲染组件的数据必须可用。所以，仅当带有 @OnDataBound 的方法被调用之后再使用 requestFocus() 这个方法。

### API

requestFocus() 方法有几种不同的形式。

**SectionLifecycle.requestFocus(SectionContext c, int index)**

指定组件在 Section 中的索引来获取焦点，其中 Section 的作用域由 SectionContext 给出。

> 跟 @OnViewportChanged 一样，这里的索引对应于组件所在Section的组件数。

#### SectionLifecycle.requestFocus(SectionContext c, String sectionKey)

指定Section 的 Key ,让此 Section 中第一个组件获取焦点。

#### SectionLifecycle.requestFocusWithOffset(SectionContext c, int index, int offset)

与 SectionLifecycle.requestFocus(SectionContext c, int index) 相同，但是带有偏移量。

#### SectionLifecycle.requestFocusWithOffset(SectionContext c, String sectionKey, int offset)

与 SectionLifecycle.requestFocus(SectionContext c, String sectionKey) 相同，但是带有偏移量。

#### SectionLifecycle.requestFocus(SectionContext c, String sectionKey, FocusType focusType)

这里的FocusType 有两个值：

1\) FocusType.START 让 Section 中的第一个组件获取焦点。

2\) FocusType.END 让 Section 中的最后一个组件获取焦点。

## @OnEvent(LoadingEvent.class)

当 Section 的子节点处于加载状态时，Section使用带有这个@OnEvent(LoadingEvent.class)注解的方法 来接受加载事件。

### API

```
class YourSectionSpec {

  @OnCreateChildren
  protected static Children onCreateChildren(SectionContext c) {
    return Children.create()
        .child(
          ChildSection
            .create(c)
            .loadingEventHandler(YourSection.onLoadingEvent(c)))
        .build()
    }
  }

  @OnEvent(LoadingEvent.class)
  static void onLoadingEvent(
    SectionContext c,
    @FromEvent LoadingState loadingState,
    @FromEvent boolean isEmpty,
    @FromEvent Throwable t) {

    switch (loadingState) {
        case INITIAL_LOAD:
        case LOADING:
          // Handle loading
          break;
        case FAILED:
          // Handle failure
          break;
        case SUCCEEDED:
          // Handle success
          break;
    }

    // Dispatch the same loading event up the hierarchy.
    SectionLifecycle.dispatchLoadingEvent(
        c,
        isEmpty,
        loadingState,
        t);
  }
}
```

#### LoadingState：

1\) INITIAL\_LOAD 开始加载。

2\) LOADING 加载进行中。

3\) SUCCEEDED 加载成功。

4\) FAILED 加载失败。

#### isEmpty：

加载完成之后数据集为空时则返回true 。

#### Throwable t

返回加载失败的原因。

### 向上传递加载事件

加载事件要向上传递直到某个 Section 决定处理此次加载事件。如果某一 Section 处理加载事件时，这个 Section 的父 Sections 也想要处理此次加载事件，则需要将事件向上传递。

```
SectionLifecycle.dispatchLoadingEvent(c, isEmpty, loadingState, t);
```
