# 如何创建带选择器的日期或时间字段

> 点击时打开 DatePicker 或 TimePicker 并按 locale 格式化的字段。

Source: https://crossfly.dev/zh/docs/cookbook/forms/data-e-hora — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 如何创建带选择器的日期或时间字段

**用途。** 创建一个字段，在被点击时打开 `DatePicker`（日历）或 `TimePicker`（时钟），并按 locale 格式填入字段。用户也可以直接用掩码输入。

## 用到的 API
- `campo.setMode(EditMode.DATE, true)` — 带 `maskedEdit` 的日期模式。根据 locale 推导掩码：`LocaleFormats.dateFormat == YMD → "9999-99-99"`，否则为 `"99/99/9999"`（使用 `LocaleFormats.dateSeparator`）；`validChars = "0123456789"`；`keyboard = DATE`。点击时打开 `DatePicker`。
- `campo.setMode(EditMode.TIME, true)` — 带掩码 `"99:99"`（使用 `LocaleFormats.timeSeparator`）的时间模式；`keyboard = TIME`。点击时打开 `TimePicker`。
- `TextField.getText()` — 带分隔符的格式化文本（`"15/03/2024"`、`"14:30"`）。
- `TextField.getTextWithoutMask()` — 只有数字（`"15032024"`、`"1430"`）。

内部行为（自动，无需额外代码）：
- 打开时，如果当前值可解析，字段会用当前值预填充选择器（日期用 `DateTimeUtils.parseDate`；时间用数字 `hhmm`）。
- 在选择器中确认时，内部的 `onSelect` 会用格式化的值调用 `setText(...)`，并将焦点恢复到文本末尾（`restoreFocusAtEnd`）。
- `DatePicker`/`TimePicker` 按实例创建一次（惰性）并被复用。

> 要手动打开选择器（在字段之外），请使用 `new DatePicker().onSelect(...).show()` 或 `new TimePicker().onSelect(...).show()`。有用的 getter：`DatePicker.getSelectedDay()/getDisplayMonth()/getDisplayYear()`、`TimePicker.getHour()/getMinute()/getTimeText()`。

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

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;   // TextFieldStyle（在样式中配置）

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

      // 日期字段 — 点击时打开 DatePicker
      TextField data = new TextField("");
      data.caption("Data");
      data.setMode(EditMode.DATE, true);

      // 时间字段 — 点击时打开 TimePicker
      TextField hora = new TextField("");
      hora.caption("Hora");
      hora.setMode(EditMode.TIME, true);

      Label resultado = new Label("");

      Button salvar = new Button("Salvar");
      salvar.onAction(o ->
      {
         String dataFmt = data.getText();            // "15/03/2024"
         String horaFmt = hora.getText();            // "14:30"
         if (dataFmt.isEmpty() || horaFmt.isEmpty())
         {
            Hint.show("Preencha data e hora", 2000);
            return;
         }
         resultado.setText("Agendado para " + dataFmt + " às " + horaFmt);
      });

      add(LAY_FILL, data);
      add(LAY_FILL, hora);
      add(LAY_AUTO, salvar);
      add(LAY_FILL, resultado);
   }
}
```

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

## 最小片段
```java
import dev.crossfly.ui.*;
import dev.crossfly.ui.style.*;
import static dev.crossfly.ui.UIConstants.EditMode;

// 日期字段 — 点击时打开 DatePicker
TextField data = new TextField("");
data.caption("Data");
data.setMode(EditMode.DATE, true);

// 时间字段 — 点击时打开 TimePicker
TextField hora = new TextField("");
hora.caption("Hora");
hora.setMode(EditMode.TIME, true);

Button salvar = new Button("Salvar");
salvar.onAction(o ->
{
   String dataFmt = data.getText();            // "15/03/2024"
   String horaFmt = hora.getText();            // "14:30"
   String dataDig = data.getTextWithoutMask(); // "15032024"
   // ... 持久化
});
```

## 深入了解
- [DatePicker](/docs/ui/widgets/datepicker) · [掩码与键盘](/docs/ui/forms-and-input/masks-and-keyboards)
- 姊妹配方：[CPF/CEP/日期掩码](/docs/cookbook/forms/mascara-cpf) · [数字键盘](/docs/cookbook/forms/teclado-numerico)
