# DatePicker

> 在月历视图中选择日期，支持限定可选范围，并回传选中的那一天。

Source: https://crossfly.dev/zh/docs/ui/widgets/datepicker — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# DatePicker

`DatePicker` 在月历中选择一个**日期**，支持按月、按年导航以及快速选择月/年的网格。它遵循 picker 模式：关闭时通过 `getAnswer()`（由 `show(onAnswer)` 消费）返回值（见 [`Picker`](/docs/ui/widgets/picker)）。

> 🟡 · **包：** `dev.crossfly.ui.picker` · **继承：** `Picker` · **样式：** `DatePickerStyle`

## 何时使用
- 用日历 UI 收集一个日期（生日、预约、到期日）。

## 何时不要使用
- 收集时间 → 使用 [`TimePicker`](/docs/ui/widgets/timepicker)。

## 外观
带标题栏的卡片、月份行（`< 名称 >`）、年份行（`< 2026 >`）以及一个 6×7 的日期单元格网格。尺寸约为 `Screen.minSize` 的 90%。

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

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

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

      Button abrir = new Button("Escolher data");
      abrir.onAction(c ->
      {
         DatePicker dp = new DatePicker().title("Data de nascimento");
         dp.show(answer -> Hint.show("Data: " + (String) answer, 2000));
      });
      add(LAY_AUTO, abrir);
   }
}
```

> 下面的代码片段只展示**核心部分**（放进 `onShow()` 内的内容）。要运行它们，请把它们放入上面的 `MainWindow` 骨架中。

## 基本用法
```java
DatePicker dp = new DatePicker();
dp.show(answer -> campoData.setText((String) answer));
```

## 构造函数
| 构造函数 | 描述 |
|---|---|
| `DatePicker()` | 无参数；以今天的日期开始。 |

## 属性与自定义（流式）
| 方法 | 类型 | 默认值 | 作用 |
|---|---|---|---|
| `setDate(int year, int month, int day)` | 年、月 **从 0 开始**、日 从 1 开始 | 今天 | 预选日期。 |
| `title(String)` | `String` | `"Date Picker"` | 标题栏文本。 |
| `yearRange(int before, int after)` | `int`, `int` | 30, 10 | 年份网格向前/向后的年数。 |
| `style(DatePickerStyle)` | 样式 | skin | 自定义样式。 |
| `getSelectedDay()` | `int` | — | 选中的日（从 1 开始）。 |
| `getDisplayYear()` / `getDisplayMonth()` | `int` | — | 显示的年 / 月（月从 0 开始）。 |

## 回调与事件
| 回调 | 触发时机 |
|---|---|
| `onSelect(Action)` | 点击某一天时（接收 `DatePicker` 本身）。 |
| `show(onAnswer)` | 关闭时，传递日期的 `String`（如果已确认）。 |

`getAnswer()` 以 `DateTimeUtils.formatDate` 的格式返回日期——**顺序（DMY/MDY/YMD）和分隔符取决于 `LocaleFormats`**，并非固定的 `dd/MM/yyyy`。

## 状态
- 在月份之间导航会将 `selectedDay` 重置为 `-1`（在用户选择之前不标记任何日期）。
- 按状态的样式：今天、选中、其他月份、正常（通过 `DatePickerStyle`）。

### 出生日期
```java
DatePicker dp = new DatePicker()
   .title("Data de nascimento")
   .yearRange(100, 0)     // 至多 100 年前，未来一概不允许
   .setDate(2000, 0, 1);  // 01/Jan/2000（月份从 0 开始！）

dp.onSelect(p ->
{
   DatePicker d = (DatePicker) p;
   Debug.log("dia=" + d.getSelectedDay() + " mes=" + (d.getDisplayMonth() + 1));
});

dp.show(answer ->
{
   String data = (String) answer; // 例如："01/01/2000"，符合 LocaleFormats
   campoData.setText(data);
});
```

## 最佳实践
- 记住在 `setDate` 中 **`month` 从 0 开始**。
- 要本地化月/日名称，请重新赋值公有常量 `MONTH_NAMES`/`MONTH_SHORT`。

## 常见错误
- **在 `setDate` 中传入从 1 开始的月份** —— 一月是 `0`。
- **期望固定格式** —— 输出遵循 `LocaleFormats.dateFormat`/`dateSeparator`。
- **把 `yearRange` 当作选择的钳制** —— 它只限制年份*导航*；所显示月份的任何一天都可选。

## 另见
- 该领域指南：[`ui/`](/docs/ui) · 食谱：[`cookbook/`](/docs/cookbook)
- 模式基类：[`Picker`](/docs/ui/widgets/picker)
- 相关组件：[`TimePicker`](/docs/ui/widgets/timepicker)
