26 日付ピッカー
この章では、JavaFXでサポートされている日付データの概要を示し、DatePicker
コントロールの基本機能について説明します。
JavaFX DatePicker
は、特定のカレンダから日付を選択できるコントロールです。これは主に、ユーザーが日付を入力する必要があるWebサイトやアプリケーションで使用されます。DatePicker
コントロールは、日付フィールドと日付チューザがあるコンボ・ボックスで構成されています。
時間データと日付書式の使用
JDK 8に導入されている新しい日時APIを使用すると、様々なタイムゾーンにわたるカレンダおよびローカル時間の設定を含む、日時データを使用した様々な操作を実行できます。
日時APIの基本パッケージはjava.time
です。これには、国際標準化機構(ISO)のカレンダに基づいて暦体系内の時間を定義するために次のクラスが用意されています。
-
Clock
- タイムゾーンを使用して現在の時点、日付および時間へのアクセスを提供するクロック -
Duration
- 時間ベースの時間量 -
Instant
- 時系列上の一瞬の点 -
LocalDate
- ISO-8601暦体系のタイムゾーンのない日付 -
LocalDateTime
- ISO-8601暦体系のタイムゾーンのない日時 -
LocalTime
- ISO-8601暦体系のタイムゾーンのない時間 -
MonthDay
- ISO-8601暦体系での日 -
OffsetDateTime
- ISO-8601暦体系でのグリニッジ/UTCからのオフセット付きの日時 -
OffsetTime
- ISO-8601暦体系でのグリニッジ/UTCからのオフセット付きの時間 -
Period
- 日付ベースの時間量 -
Year
- ISO-8601暦体系での年 -
YearMonth
- ISO-8601暦体系での年月 -
ZonedDateTime
- ISO-8601暦体系のタイムゾーンのある日時 -
ZoneId
- タイムゾーンID -
ZoneOffset
- グリニッジ/UTCからのタイムゾーンのオフセット
使用可能な機能およびコード・サンプルの詳細は、Javaチュートリアルの日時API」トレールに関する項を参照してください。
日付ピッカーの設計の概要
JDK 8の日時APIの新機能を使用してJavaFXアプリケーションのユーザー・インタフェースを拡張するために、JavaFX SDKにはDatePicker
コントロールが導入されています。DatePicker
コントロールは、図26-1に示す編集可能なコンボ・ボックス(日付フィールド)とカレンダ(日付チューザ)で構成されています。
アプリケーションUIへの日付ピッカーの追加
図26-1に示すように、javafx.scene.control
パッケージに用意されているDatePicker
クラスを使用して、JavaFXアプリケーションのユーザー・インタフェース(UI)に日付ピッカー・コンポーネントを追加します。
例26-1 日付ピッカー・コンポーネントの追加
import java.util.Locale; import javafx.application.Application; import javafx.geometry.HPos; import javafx.scene.Scene; import javafx.scene.control.DatePicker; import javafx.scene.control.Label; import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class DatePickerSample extends Application { private Stage stage; private DatePicker checkInDatePicker; public static void main(String[] args) { Locale.setDefault(Locale.US); launch(args); } @Override public void start(Stage stage) { this.stage = stage; stage.setTitle("DatePickerSample "); initUI(); stage.show(); } private void initUI() { VBox vbox = new VBox(20); vbox.setStyle("-fx-padding: 10;"); Scene scene = new Scene(vbox, 400, 400); stage.setScene(scene); checkInDatePicker = new DatePicker(); GridPane gridPane = new GridPane(); gridPane.setHgap(10); gridPane.setVgap(10); Label checkInlabel = new Label("Check-In Date:"); gridPane.add(checkInlabel, 0, 0); GridPane.setHalignment(checkInlabel, HPos.LEFT); gridPane.add(checkInDatePicker, 0, 1); vbox.getChildren().add(gridPane); } }
例26-1では、ホテル予約のチェックイン日を選択するための一般的なUIを実装しています。アプリケーションをコンパイルして実行すると、図26-2に示すコンポーネントが表示されます。
日付フィールドの初期状態は空であることに注意してください。ただし、この動作を変更し、日付が選択される前に日付値が表示されるよう指定することもできます。DatePicker
コンストラクタにvalue
プロパティを設定するか、ComboBox
クラスから継承されたsetValue
メソッドをコールします。例26-2に、LocalDate
値を設定するためのオプションをいくつか示します。
例26-2 日付値の設定
//Setting a particular date value in the class constructor checkInDatePicker = new DatePicker(LocalDate.of(1998, 10, 8)); //Setting a particular date value by using the setValue method checkInDatePicker.setValue(LocalDate.of(1998, 10, 8)); //Setting the minimum date available in the calendar checkInDatePicker.setValue(LocalDate.MIN); //Setting the maximum date available in the calendar checkInDatePicker.setValue(LocalDate.MAX); //Setting the current date checkInDatePicker.setValue(LocalDate.now());
初期値を設定すると、アプリケーションをコンパイルして実行した後に日付フィールドに表示されます。図26-3に、日付フィールドに指定した初期日付を示します。
DatePicker
APIには、日付ピッカーのデフォルトの外観と動作を変更するために複数のプロパティおよびメソッドが用意されています。特に、週番号の表示の切替え、日付書式のカスタマイズ、および日付セル・ファクトリの定義と適用が可能です。
日付ピッカーのカスタマイズ
カレンダでISOの週番号の表示を有効または無効にするには、DatePicker
クラスのsetShowWeekNumbers
メソッドを使用します。例26-3は、このタスクをcheckInDatePickerに実装するコード行です。
この変更により、図26-4に示すように、カレンダ要素に週番号が追加された日付ピッカー・コンポーネントが生成されます。
デフォルトでは、日付フィールドの日付は、システム・ロケールおよびISO暦体系によって定義された書式で表示されます。このため、例26-1で選択した日付は、mm/dd/yyyyの書式で表示されます。通常、このデフォルトの書式を変更する必要はありません。ただし、必要な場合は、DatePicker
クラスのconverter
プロパティおよびsetConverter
メソッドを使用して、別の日付書式を設定できます。converter
値をnull
に設定すると、デフォルトの日付書式に戻ります。
例26-4では、yyyy-MM-ddのパターンに応じて日付の書式を変更するためのコンバータを作成しています。これにより、ユーザーが日付フィールドに日付を入力したときに、入力テキストをLocalDate
タイプのオブジェクトに変換します。また、カレンダで選択した日付に対応するLocalDate
オブジェクトを、日付フィールドに表示されるテキストに変換します。
例26-4 日付書式の変換
import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.Locale; import javafx.application.Application; import javafx.geometry.HPos; import javafx.scene.Scene; import javafx.scene.control.DatePicker; import javafx.scene.control.Label; import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; import javafx.util.StringConverter; public class DatePickerSample extends Application { private Stage stage; private DatePicker checkInDatePicker; private final String pattern = "yyyy-MM-dd"; public static void main(String[] args) { Locale.setDefault(Locale.US); launch(args); } @Override public void start(Stage stage) { this.stage = stage; stage.setTitle("DatePickerSample "); initUI(); stage.show(); } private void initUI() { VBox vbox = new VBox(20); vbox.setStyle("-fx-padding: 10;"); Scene scene = new Scene(vbox, 400, 400); stage.setScene(scene); checkInDatePicker = new DatePicker(); StringConverter converter = new StringConverter<LocalDate>() { DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(pattern); @Override public String toString(LocalDate date) { if (date != null) { return dateFormatter.format(date); } else { return ""; } } @Override public LocalDate fromString(String string) { if (string != null && !string.isEmpty()) { return LocalDate.parse(string, dateFormatter); } else { return null; } } }; checkInDatePicker.setConverter(converter); checkInDatePicker.setPromptText(pattern.toLowerCase()); GridPane gridPane = new GridPane(); gridPane.setHgap(10); gridPane.setVgap(10); Label checkInlabel = new Label("Check-In Date:"); gridPane.add(checkInlabel, 0, 0); GridPane.setHalignment(checkInlabel, HPos.LEFT); gridPane.add(checkInDatePicker, 0, 1); vbox.getChildren().add(gridPane); checkInDatePicker.requestFocus(); } }
例26-4では、日付書式を変換するだけでなく、必要な日付書式についてユーザーに通知するためのプロンプト・テキストを日付フィールドに設定しています。図26-5に、日付書式コンバータが適用された日付ピッカーの2つの状態を示します。プロンプト・テキストと選択した日付の両方が新しい書式で表示されています。
デフォルトの外観を変更したり、カレンダ要素の1つ以上のセルに特定の動作を設定することもできます。このタスクを実装するには、実際のユースケース(ホテルの予約時におけるチェックイン日とチェックアウト日の選択)について考えてみます。例26-5では、2つの日付ピッカーを使用して一般的なユーザー・インタフェースを作成しています。
例26-5 2つの日付ピッカー・コンポーネントの追加
import java.time.LocalDate; import java.util.Locale; import javafx.application.Application; import javafx.geometry.HPos; import javafx.scene.Scene; import javafx.scene.control.DatePicker; import javafx.scene.control.Label; import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class DatePickerSample extends Application { private Stage stage; private DatePicker checkInDatePicker; private DatePicker checkOutDatePicker; public static void main(String[] args) { Locale.setDefault(Locale.US); launch(args); } @Override public void start(Stage stage) { this.stage = stage; stage.setTitle("DatePickerSample "); initUI(); stage.show(); } private void initUI() { VBox vbox = new VBox(20); vbox.setStyle("-fx-padding: 10;"); Scene scene = new Scene(vbox, 400, 400); stage.setScene(scene); checkInDatePicker = new DatePicker(); checkOutDatePicker = new DatePicker(); checkInDatePicker.setValue(LocalDate.now()); checkOutDatePicker.setValue(checkInDatePicker.getValue().plusDays(1)); GridPane gridPane = new GridPane(); gridPane.setHgap(10); gridPane.setVgap(10); Label checkInlabel = new Label("Check-In Date:"); gridPane.add(checkInlabel, 0, 0); GridPane.setHalignment(checkInlabel, HPos.LEFT); gridPane.add(checkInDatePicker, 0, 1); Label checkOutlabel = new Label("Check-Out Date:"); gridPane.add(checkOutlabel, 0, 2); GridPane.setHalignment(checkOutlabel, HPos.LEFT); gridPane.add(checkOutDatePicker, 0, 3); vbox.getChildren().add(gridPane); } }
例26-5のcheckInDatePickerの事前定義済の値は、システム・クロックの現在の日付に対応するLocalDate.now()
です。checkOutDatePickerの事前定義済の日付は、現在の日付の翌日です。
この例を2013年9月19日でコンパイルして実行すると、図26-6に示す出力が生成されます。
デフォルトでは、カレンダ要素内のすべてのセルが選択可能です。この場合、チェックアウト日がチェックイン日の前になる可能性がありますが、これは正しくありません。
例26-6では、checkInDatePickerで選択した日付に対応するセル、およびこれより前の日付に対応するすべてのセルを無効にするために、checkOutDatePickerに対して日付セル・ファクトリを作成しています。
例26-6 特定の日付を無効にするための日付セル・ファクトリの実装
import java.time.LocalDate; import java.util.Locale; import javafx.application.Application; import javafx.geometry.HPos; import javafx.scene.Scene; import javafx.scene.control.DateCell; import javafx.scene.control.DatePicker; import javafx.scene.control.Label; import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; import javafx.util.Callback; public class DatePickerSample extends Application { private Stage stage; private DatePicker checkInDatePicker; private DatePicker checkOutDatePicker; public static void main(String[] args) { Locale.setDefault(Locale.US); launch(args); } @Override public void start(Stage stage) { this.stage = stage; stage.setTitle("DatePickerSample "); initUI(); stage.show(); } private void initUI() { VBox vbox = new VBox(20); vbox.setStyle("-fx-padding: 10;"); Scene scene = new Scene(vbox, 400, 400); stage.setScene(scene); checkInDatePicker = new DatePicker(); checkOutDatePicker = new DatePicker(); checkInDatePicker.setValue(LocalDate.now()); final Callback<DatePicker, DateCell> dayCellFactory = new Callback<DatePicker, DateCell>() { @Override public DateCell call(final DatePicker datePicker) { return new DateCell() { @Override public void updateItem(LocalDate item, boolean empty) { super.updateItem(item, empty); if (item.isBefore( checkInDatePicker.getValue().plusDays(1)) ) { setDisable(true); setStyle("-fx-background-color: #ffc0cb;"); } } }; } }; checkOutDatePicker.setDayCellFactory(dayCellFactory); checkOutDatePicker.setValue(checkInDatePicker.getValue().plusDays(1)); GridPane gridPane = new GridPane(); gridPane.setHgap(10); gridPane.setVgap(10); Label checkInlabel = new Label("Check-In Date:"); gridPane.add(checkInlabel, 0, 0); GridPane.setHalignment(checkInlabel, HPos.LEFT); gridPane.add(checkInDatePicker, 0, 1); Label checkOutlabel = new Label("Check-Out Date:"); gridPane.add(checkOutlabel, 0, 2); GridPane.setHalignment(checkOutlabel, HPos.LEFT); gridPane.add(checkOutDatePicker, 0, 3); vbox.getChildren().add(gridPane); } }
checkOutDatePickerに適用されたdayCellFactory
は、setDisable
およびsetStyle
メソッドをDateCell
項目に対してコールし、特定のセルを選択できないようにし、これらをピンク色にしています。
例26-6のコードをコンパイルして実行すると、DatePickerSampleでは、図26-7に示すように、所定の動作でUIが生成されます。
ここまで、日付セル・ファクトリの作成方法について学習しました。これで、checkOutDatePickerのデフォルトの動作を拡張し、選択可能な日付セルに追加機能を設定できるようになります。
例26-7では、checkInDatePickerで選択した日付と現在の日付セルの間の日付間隔を計算しています。この結果は、セル・ツールチップでレンダリングされます。
例26-7 時間間隔の計算
import java.time.LocalDate; import java.time.temporal.ChronoUnit; import java.util.Locale; import javafx.application.Application; import javafx.geometry.HPos; import javafx.scene.Scene; import javafx.scene.control.DateCell; import javafx.scene.control.DatePicker; import javafx.scene.control.Label; import javafx.scene.control.Tooltip; import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; import javafx.util.Callback; public class DatePickerSample extends Application { private Stage stage; private DatePicker checkInDatePicker; private DatePicker checkOutDatePicker; public static void main(String[] args) { Locale.setDefault(Locale.US); launch(args); } @Override public void start(Stage stage) { this.stage = stage; stage.setTitle("DatePickerSample "); initUI(); stage.show(); } private void initUI() { VBox vbox = new VBox(20); vbox.setStyle("-fx-padding: 10;"); Scene scene = new Scene(vbox, 400, 400); stage.setScene(scene); checkInDatePicker = new DatePicker(); checkOutDatePicker = new DatePicker(); checkInDatePicker.setValue(LocalDate.now()); final Callback<DatePicker, DateCell> dayCellFactory = new Callback<DatePicker, DateCell>() { @Override public DateCell call(final DatePicker datePicker) { return new DateCell() { @Override public void updateItem(LocalDate item, boolean empty) { super.updateItem(item, empty); if (item.isBefore( checkInDatePicker.getValue().plusDays(1)) ) { setDisable(true); setStyle("-fx-background-color: #ffc0cb;"); } long p = ChronoUnit.DAYS.between( checkInDatePicker.getValue(), item ); setTooltip(new Tooltip( "You're about to stay for " + p + " days") ); } }; } }; checkOutDatePicker.setDayCellFactory(dayCellFactory); checkOutDatePicker.setValue(checkInDatePicker.getValue().plusDays(1)); GridPane gridPane = new GridPane(); gridPane.setHgap(10); gridPane.setVgap(10); Label checkInlabel = new Label("Check-In Date:"); gridPane.add(checkInlabel, 0, 0); GridPane.setHalignment(checkInlabel, HPos.LEFT); gridPane.add(checkInDatePicker, 0, 1); Label checkOutlabel = new Label("Check-Out Date:"); gridPane.add(checkOutlabel, 0, 2); GridPane.setHalignment(checkOutlabel, HPos.LEFT); gridPane.add(checkOutDatePicker, 0, 3); vbox.getChildren().add(gridPane); } }
変更したDatePickerSampleアプリケーションをコンパイルして実行すると、日付セルの上にマウス・カーソルを置いたときにツールチップが表示されます。図26-8は、9月30日のセルの上にマウス・カーソルを置いた瞬間を捉えています。このツールチップは、9月19日と9月30日の間の日数が11日間であることを示しています。
暦体系の変更
JDK 8に導入されている日時Java APIを使用すると、開発者は、ISOベースの暦体系だけでなく、和暦、イスラム暦、民国暦およびタイ仏暦などの別の暦体系も使用できます。
DatePicker
コントロールは、適切に年をレンダリングすることによってISO以外の暦体系もサポートしています。月の日付がISOの日付とは異なるイスラム暦の場合、ISOとイスラム暦の日付を区別するために別の視覚テーマが用意されています。
例26-8では、タイ仏暦をcheckInDatePickerに適用し、イスラム暦をcheckOutDatePickerに適用しています。
例26-8 タイ仏暦とイスラム暦の適用
import java.time.chrono.*; checkInDatePicker.setChronology(ThaiBuddhistChronology.INSTANCE); checkOutDatePicker.setChronology(HijrahChronology.INSTANCE);
これらの行をDatePickerSampleアプリケーションに適用すると、図26-9に示すように、日付ピッカー・コントロールの外観が変更されます。
ISO以外の日付のサポートの詳細は、Javaチュートリアルの対応するレッスンを参照してください。
関連ドキュメント