# 如何使 Component 可滚动并调整 overscroll

> 使用 setScrollable 在任何 Component 上启用滚动，并配置边缘弹性效果。

Source: https://crossfly.dev/zh/docs/cookbook/lists/rolagem-e-overscroll — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 如何使 Component 可滚动并调整 overscroll

**目的。** 任何 `Component` 都可以变成可滚动的容器：只需用
`setScrollable(ScrollDirection)` 声明方向。如果内容（子元素高度之和）超出了可见区域，
`ScrollEngine` 会处理 pan、动量、fling 以及 **overscroll**（拉动超过限制时的弹性效果）。

## 使用的 API
- `Component.setScrollable(ScrollDirection dir, ScrollEngine... engine)` — 启用滚动。`engine` 是可选的（默认：新建 `ScrollEngine`）。传入 `null` 作为 `dir` 以禁用。
- `UIConstants.ScrollDirection` — 枚举，有**三个**值：`HORIZONTAL`、`VERTICAL`、`BOTH`。
- `Component.isScrollable()` — 组件是否可滚动。
- `Component.scrollable()` — 返回 `Scrollable` 对象（含 `.engine`），或 `null`。
- `Component.setShowScrollPosition(boolean)` — 显示/隐藏滚动位置指示器。
- `ScrollEngine.setOverscroll(boolean x, boolean y)` — 按轴开启/关闭弹性效果。默认：两者均为 `true`。
- `ScrollEngine.setupOverscroll(float distance, float speedMin, float speedMax)` — 调整 overscroll 的回弹距离和速度。
- `ScrollEngine.setScrollListener(ScrollListener listener)` — 每次偏移变化时的回调（接收 `vx, vy`）。
- `ScrollEngine.scrollTo(float x, float y)` / `scrollToStartSmooth()` / `scrollToEndSmooth()` — 重新定位滚动位置。
- `ScrollEngine.setOverscrollEffect(OverscrollEffect effect)` — **静态**：定义/禁用（`null`）全局边缘灰色曲线；可通过 `new OverscrollEffect().setColor(...).setMaxAlpha(...).setMaxArcHeight(...)` 自定义。

## 完整示例（复制、粘贴并运行）
```java
package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;

/** 可执行的主界面。将 launcher 指向此类（classname）并运行：gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      Component painel = new Component();
      painel.setLayout(new GridLayout(Position.CENTER, Position.START));
      for (int i = 0; i < 60; i++)
         painel.add(.95f, new Label("Linha " + i));

      painel.setScrollable(ScrollDirection.VERTICAL);   // 沿 Y 轴滚动

      // 滚动引擎的可选调整：
      painel.scrollable().engine.setOverscroll(true, false);   // 仅垂直方向弹性
      painel.setShowScrollPosition(true);                       // 位置指示器
      painel.scrollable().engine.setScrollListener((vx, vy) ->
         Debug.log("scrollY = " + vy));                         // 响应滚动

      add(LAY_FILL, painel);
   }
}
```

> 下面的片段只展示**核心部分**。要运行它，请将其放入上面的 `MainWindow` 骨架中。

## 最小片段
```java
// 已在源码核实：Component.setScrollable（约 1027 行）+ ScrollEngine.java
Component painel = new Component();
painel.setLayout(new GridLayout(Position.CENTER, Position.START));
for (int i = 0; i < 60; i++)
   painel.add(.95f, new Label("Linha " + i));

painel.setScrollable(ScrollDirection.VERTICAL);   // 沿 Y 轴滚动

// 滚动引擎的可选调整：
painel.scrollable().engine.setOverscroll(true, false);   // 仅垂直方向弹性
painel.setShowScrollPosition(true);                       // 位置指示器
painel.scrollable().engine.setScrollListener((vx, vy) ->
   Debug.log("scrollY = " + vy));                         // 响应滚动

layout.add(1f, painel);
```

通过代码重新定位：

```java
painel.scrollable().engine.scrollToStartSmooth();   // 带动画回到顶部
painel.scrollable().engine.scrollTo(0, 500);        // 跳转到偏移 y=500 px
```

## 诚实提示：没有现成的 pull-to-refresh

框架**不**提供现成的 "pull-to-refresh"（下拉刷新）小部件。现有的是
**overscroll** 效果（拉动超过限制时的弹性曲线），它纯粹是**视觉/物理**性的——
它不会暴露一个"拉动 X 像素后松手"的回调供你触发刷新。如果需要该行为，你必须
通过 `setScrollListener` 监控偏移量并检测超过某个阈值的 overscroll 来手动构建它。
对于需要重新加载数据的大型列表，最好使用由按钮或自定义手势触发的
`VirtualListView.reloadData()`。

## 深入了解
- 超大列表默认已可滚动（`VirtualListView` 内部会调用 `setScrollable(VERTICAL)`）：参见 [lista-virtual-grande.md](/docs/cookbook/lists/lista-virtual-grande)。
