torutkのブログ

ソフトウェア・エンジニアのブログ

JavaFXでカレンダー表示プログラムを作る(DatePickerのポップアップ利用)

This article explains a tiny calendar view program by JavaFX 8/9 using DatePickerSkin class. The following are the Japanese text version.

先月、自宅のメインPCのOSをWindows 7からWindows 10へアップグレードしました。
Windows 7には、デスクトップにちょっとしたツールを配置するガジェット(スマートフォンでいえばウィジェットに相当)がありました。しかし、セキュリティ脆弱性からWindows 8以降では廃止となり*1、必要なツールは個別にWindowsアプリケーションを揃えることが必要となりました。

ここで、ガジェットで利用していた単純な機能であれば、プログラミングの練習を兼ねて自分でプログラムを作ってしまおうと思いました。

第1弾は、Windows 7のガジェットではほとんどの環境で使っていたアナログ時計で、2015年春にJJUG CCCでJavaFXアニメーションプログラミングの紹介した際の題材として作成しました。
Java FXグラフィックスとアニメーション入門(JJUG CCC 2015 Spring G-7)

このアナログ時計作成については、その後タッチパネル対応やCPU/メモリ使用削減などの改善を行い、その経緯は日記でも何回か言及しています。

今回、第2弾として、カレンダーを表示してみようと取り組み始めました。

JavaFXでのカレンダー表示

JavaFXでは、標準でDatePickerと呼ぶ日付指定用コントロールが搭載されています。

これはコンボボックスの一種で、コンボボックス右端のボタンを押すとカレンダ月間表示がポップアップされ、任意の日付を指定するとその年月日がコンボボックスに入力されるというものです。

この機能のうち、月間カレンダ表示の部分だけを常時表示させたいのですが、ぱっと見た限りではその方法が見出せませんでした。

TableViewを使って見出しのTableColumnに曜日を、Tableのセルに日付を表示させようとの試みもしましたが、複雑になってどうもうまくいかない感じがしていました。

そんなときに、DatePickerを使ってポップアップ表示を常時画面に貼り付ける方法が見つかりました*2

JavaFX 8では公開APIではない(Javadocに未記載)com.sun.javafx.scene.control.skin.DatePickerSkinを使いますが、JavaFX 9ではjavafx.scene.control.skin.DatePickerSkinとして公開APIに含まれる予定です。

最小限のカレンダー表示コード

JDK 9 Early Access b125版で実装・動作確認をしました。
JDK 8で実行する場合は、DatePickerSkinのimport文を1行修正します。

import com.sun.javafx.scene.control.skin.DatePickerSkin;
import java.time.LocalDate;
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.DatePicker;
import javafx.scene.control.skin.DatePickerSkin;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class Calendar extends Application {

    public void start(Stage primaryStage) {
	DatePickerSkin skin = new DatePickerSkin(new DatePicker(LocalDate.now()));
	Node calendar = skin.getPopupContent();

	StackPane root = new StackPane();
	root.getChildren().add(calendar);
	Scene scene = new Scene(root);

	primaryStage.setTitle("Calendar");
	primaryStage.setScene(scene);
	primaryStage.show();
    }
    
}

JDK 9 b125をインストールしたマシンでコンパイル・実行します。

$ javac Calendar.java
$ java Calendar

実行すると次の画面が表示されます。

今回のプログラム

JDK 8で実行するプログラム(NetBeans IDEのプロジェクト設定一式)をGithubに置いています。

https://github.com/torutk/calendar/tree/v0.1.0

今後の課題

デスクトップにガジェット代わりに表示するカレンダーとしては、まだまだ改修点があります。

  • 画面サイズを小さくできるようにする

現状は、ウィンドウサイズを小さくすると、文字が[…]となってしまいます。

画面のサイズを小さくしたときは、それに合わせて文字を小さくして表示するようにしたいところです。

  • ウィンドウのタイトルバーを非表示にする

ウィンドウのタイトルバーはデスクトップの脇に貼っておくには邪魔なので非表示にしたいところです。その場合、ウィンドウの移動、サイズ変更、終了の操作をプログラム側で用意する必要があります。

  • 表示の色、フォント

休日の色を変えたい、前月/翌月の日付は薄くしたい、などの対応ができるといいかもしれません。
また、フォントも好みで変えられるといいかもしれません。

DatePickerのCSSは、JavaFX CSSリファレンスにはほとんど記載されていないので、JavaFXのランタイムに含まれるmodena.cssを抜粋し、その中にあるDatePickerの記述を調べて確認するのがよいようです。

modena.cssの抜粋方法は、次のドキュメントに記載があります。jarコマンドでなくてもzipツールで取り出すことは可能です。
37 CSSによるUIコントロールのスタイル設定(リリース8)

modena.cssに記載されている例を以下に抜粋

.date-picker-popup {
     -fx-background-color:
        linear-gradient(to bottom,
            derive(-fx-color,-17%),
            derive(-fx-color,-30%)
        ),
        -fx-control-inner-background;
    -fx-background-insets: 0, 1;
    -fx-background-radius: 0;
    -fx-alignment: CENTER; /* VBox */
    -fx-spacing: 0; /* VBox */
    -fx-padding: 0.083333em; /* 1 1 1 1 */
    -fx-effect: dropshadow( gaussian , rgba(0,0,0,0.2) , 12, 0.0 , 0 , 8 );
}

.date-picker-popup > * > .day-cell {
    -fx-padding: 0.333333em 0.583333em 0.333333em 0.583333em; /* 4 7 4 7 */
    -fx-border-color: derive(-fx-selection-bar-non-focused, 60%);
    -fx-border-width: 1px;
    -fx-font-size: 1em;
    -fx-background: -fx-control-inner-background;
    -fx-background-color: -fx-background;
    -fx-text-fill: -fx-text-background-color;
}
  • プログラムを実行中日付が変わったら「今日」の表示日付も変更する

現在は、プログラムを実行したときに取得したLocalDateの日付を今日として表示します。実行中に日付が変わったことを検知し、今日の表示位置を変える処理が必要です。

*1:Windows 8以降でもガジェットを表示させるフリーのツールがあるようですが、セキュリティ脆弱性が解決されたものではないようです。

*2:またもやstackoverflowです。