跳到内容
CrossFly 是 100% 开源在 GitHub 关注我们
CrossFlyCrossFly
开始使用
全部示例
配方输入123 行 · 1 个文件

Picking a date range

DatePicker answers "which day". This answers "between which two", which has its own rules — and its own ways of being wrong.

它展示了什么

A period, not a day: inclusive at both ends, locale-aware, and it refuses dates that do not exist.

在你的机器上运行

每个示例都是普通的 Window,启动器只需要它的名字。

gradlew lwjgl3:run --args="classname dev.cf.recipe.R07DateRange"
修改它

Java 是提前编译的,浏览器无法自行重建你的修改 —— 这里的代码在发布前已由 TeaVM 转成 JavaScript。把文件复制到项目里,它在四个平台上原样运行;上面那条命令就是全部准备工作。

正在此处运行CrossFly 0.1.32
正在启动应用…

由 TeaVM 编译为 JavaScript —— 是同一份代码,不是录像。 · 全部示例

它用到了什么

本示例触及的框架类型。带链接的有参考页。

ButtonCardComponentDateRangePickerDebugGapLabelLocaleFormatsScreenStylesWidgets

源码

完整无删减。这就是那个文件,不是片段。

原始
dev/cf/recipe/R07DateRange.java
package dev.cf.recipe;

import dev.cf.Tones;

import dev.crossfly.ui.*;
import dev.crossfly.ui.color.*;
import dev.crossfly.ui.graphics.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.picker.*;
import dev.crossfly.ui.style.*;
import dev.crossfly.util.LocaleFormats;

import static dev.crossfly.ui.Widgets.*;

/**
 * A period, not a day: inclusive at both ends, locale-aware, and it refuses dates that do not exist.
 */
public class R07DateRange extends Recipe
{
   private Label resultado;

   public String title()
   {
      return "Picking a date range";
   }

   public String note()
   {
      return "DatePicker answers \"which day\". This answers \"between which two\", which has its own"
         + " rules — and its own ways of being wrong.";
   }

   public void build(Component body) throws Exception
   {
      resultado = label("no period yet", 1.2f, true, Tones.accent(), Position.START);
      resultado.testTag("recipe.range");

      Button abrir = Tones.action("Choose a period");
      abrir.testTag("recipe.range.open");
      abrir.onAction(c -> abrir());
      body.add(LAY_AUTO, outgap(abrir, 0, 0, 0, 1f));
      body.add(LAY_AUTO, resultado);

      // The rules are public and pure: they can be tested with no window at all, which is where
      // most of the defects of a control like this really live.
      // The rules go on a surface of their own: they are the OTHER half of the recipe, not a
      // footnote to the picker above, and loose grey lines read as a footnote.
      Card regras = Tones.card(Tones.support());
      regras.getStyle()
         .outgap(Gap.ofFont(0, 1.5f, 0, 0));
      regras.add(LAY_AUTO, Tones.head(0xE6B1, "The rules, with no UI involved", Tones.support()));
      linha(regras, dia(1, 7, 2026) + " to " + dia(31, 7, 2026) + " is "
         + DateRangePicker.dayCount("2026-07-01", "2026-07-31") + " days");
      linha(regras, "a day to itself is " + DateRangePicker.dayCount("2026-07-27", "2026-07-27")
         + " day, not zero");
      linha(regras, dia(31, 2, 2026) + " parses to " + DateRangePicker.toIso(dia(31, 2, 2026)));
      linha(regras, dia(29, 2, 2024) + " parses to " + DateRangePicker.toIso(dia(29, 2, 2024)));
      body.add(LAY_AUTO, regras);
   }

   /**
    * A day, month and year written the way THIS device writes dates.
    *
    * <p>{@link DateRangePicker#toIso(String)} reads a masked date in the order the device uses, so
    * a demonstration of it cannot spell its own examples in one fixed order. These four lines were
    * written as "31/02" and "29/02/2024", which is day-first: on a month-first device the reader
    * was told that a real leap day "parses to null" — true of the string as written there, and
    * exactly the opposite of the rule the line exists to show. On a year-first device both were
    * nonsense. Same three numbers, formatted for whoever is reading, and the rules hold anywhere.</p>
    *
    * @param d day, 1-based
    * @param m month, 1-based
    * @param y full year
    * @return the date in the device's own order and separator
    */
   private static String dia(int d, int m, int y)
   {
      // The parts are arranged by hand rather than through DateTimeUtils.formatDate because that
      // one validates, and one of the dates below does not exist on purpose — printing it is the
      // whole point of the line it appears in.
      String dd = d < 10 ? "0" + d : String.valueOf(d);
      String mm = m < 10 ? "0" + m : String.valueOf(m);
      char sep = LocaleFormats.dateSeparator;
      switch (LocaleFormats.dateFormat)
      {
         case MDY:
            return mm + sep + dd + sep + y;
         case YMD:
            return String.valueOf(y) + sep + mm + sep + dd;
         default: // DMY
            return dd + sep + mm + sep + y;
      }
   }

   private void linha(Component body, String t) throws Exception
   {
      body.add(LAY_AUTO, outgap(label(t, 1f, false, Styles.captionColor, Position.START),
         0, 0, 0, .3f));
   }

   private void abrir()
   {
      try
      {
         new DateRangePicker().title("Custom period")
            .subtitle("Both ends are included.")
            .show(a ->
            {
               // null = dismissed. The dates come back in ISO, whatever order is displayed.
               DateRangePicker.Range r = (DateRangePicker.Range) a;
               resultado.setText(r == null ? "dismissed"
                  : r.from + " to " + r.to + " (" + r.days + " days)");
               resultado.setValidLayout(false);
               Screen.repaint();
            });
      }
      catch (Exception e)
      {
         dev.crossfly.util.Debug.handle(e);
      }
   }
}
给 AI 助手

本页的机器可读版本,让模型直接读取示例集,而不是猜测 API。

本示例(JSON)本示例(Markdown)
上一个Choosing from a list下一个Reordering a list