# 掩码与键盘

> EditMode（日期/时间/货币）、带 '9' 的掩码、原生键盘以及内置的 Keypad。

Source: https://crossfly.dev/zh/docs/ui/forms-and-input/masks-and-keyboards — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 掩码与键盘

字段的 `EditMode` **同时控制两件事**：**格式化**（掩码）和打开的**键盘**。它是结构化输入的
核心部件。

## `EditMode` → 键盘
| `EditMode` | 格式化 | 打开的键盘 |
|---|---|---|
| `NORMAL` | 无 | 操作系统默认键盘 |
| `NUMERIC` | 数字 | 原生数字键盘（桌面上为 `Keypad`） |
| `DATE` | 日期掩码 | **DatePicker** |
| `TIME` | 时间掩码 | **TimePicker** |
| `CURRENCY` | 货币（分隔符） | **Keypad** 计算器 |
| `PASSWORD` | 隐藏字符 | 默认键盘 |

## 完整示例（复制、粘贴并运行）
```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().caption("Data");
      data.setMode(EditMode.DATE, true);      // maskedEdit=true → 格式化并打开 DatePicker
      add(LAY_AUTO, data);

      // 货币字段：输入时格式化为货币
      TextField valor = new TextField().caption("Valor");
      valor.setMode(EditMode.CURRENCY, true);
      valor.<TextFieldStyle>getStyle().decimalPlaces(2);
      add(LAY_AUTO, valor);

      // 用于显示读取值的标签
      Label resultado = new Label("");
      add(LAY_AUTO, resultado);

      // 没有按键回调：在提交时读取 getText()。
      // getTextWithoutMask() 只返回数字（适合存入数据库）。
      Button enviar = new Button("Enviar");
      enviar.onAction(c -> resultado.setText(
         "Data = " + data.getText() + "  |  Valor = " + valor.getTextWithoutMask()));
      add(LAY_AUTO, enviar);
   }
}
```

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

## 启用自动掩码
`setMode(mode, true)` 开启实时格式化（`maskedEdit`）：
```java
TextField data = new TextField().caption("Data");
data.setMode(EditMode.DATE, true);         // 输入时变为 15/03/2024 并打开 DatePicker

TextField valor = new TextField().caption("Valor");
valor.setMode(EditMode.CURRENCY, true);    // 输入时格式化为货币
valor.<TextFieldStyle>getStyle().decimalPlaces(2);
```

## 自定义掩码（`'9'` 表示数字位）
在掩码中，每个 `'9'` 是一个数字位；其余字符为固定的字面量：
```java
TextField cpf = new TextField().caption("CPF");
cpf.<TextFieldStyle>getStyle()
   .mask("999.999.999-99".toCharArray())    // 11 位数字 + 分隔符
   .mode(EditMode.NUMERIC, true);
// getText()          -> "123.456.789-00"  （带掩码）
// getTextWithoutMask()-> "12345678900"    （仅数字）
```
现成的货币：`TextField.getDefaultCurrencyMask(int casasDecimais)` 使用 locale 的千位/小数
分隔符生成掩码。

## 显式选择键盘
```java
campo.keyboard(Keyboard.NUMERIC);   // 强制数字
campo.keyboard(Keyboard.NONE);      // 从不弹出键盘（视觉上的只读字段）
```
`Keyboard`（在 `UIConstants` 中）：`NONE, DEFAULT, NUMERIC, KEYPAD, CALCULATOR, DATE, TIME`。`DEFAULT`
采用当前 `EditMode` 隐含的键盘。

## 内置 Keypad
`Keyboard.KEYPAD`/`CALCULATOR` 使用 CrossFly 自身的 **`Keypad`** 组件（而非操作系统的键盘）——在
自助终端/POS 中很有用。它由字段自动打开；如需单独使用：
```java
new Keypad(meuCampo).show();        // 字段启用数字键盘
```

## 最佳实践
- 在数据库中保存 **`getTextWithoutMask()`**（原始数字）；显示时带掩码。
- 对于证件（CPF、CEP、电话），将 `mask(...)` 与 `mode(EditMode.NUMERIC, true)` 结合使用。
- 在桌面上，`NUMERIC` 会自动回退到 `Keypad`——不要假设使用原生键盘。

## 常见错误
- 开启 `mode(EditMode.DATE)` 但**不带** `maskedEdit=true` → 无自动格式化。
- 当后端只需要数字时持久化 `getText()`（带点/破折号）。
- 掩码中没有任何 `'9'` → 不接受任何数字。

## 另见
- [文本字段](/docs/ui/forms-and-input/text-fields) · 目录：[DatePicker](/docs/ui/widgets/datepicker) · [TimePicker](/docs/ui/widgets/timepicker) · [Keypad](/docs/ui/widgets/keypad)
