環境
- JDK 1.8.0_121
- ControlsFX 8.40.12
ControlsFXにおけるバリデーション
JavaFXアプリケーションに入力チェック機能を実装するためにControlsFXのバリデーション機能を使ってみます。以下のような見た目になります。
エラーアイコンにカーソルを合わせるとエラーメッセージがツールチップ表示されます。(ちょっと気づきにくい感じはします)
準備
ControlsFXを使用するのでGradleやMavenのdependencyにControlsFXを追加します。直接インポートする場合はこちらからダウンロードしてください。
Validator
Validator
は入力内容をチェックするための基本的な仕組みを提供しており、自分で条件を定義することもできます。以下の4つのメソッドを使ってバリデーターを作成できます。
Validator<T>:: | 動作 |
---|---|
createEmptyValicator | 空入力をERRORにする |
createEqualsValidator | リストにない入力をERRORにする |
createRegexValidator | 正規表現に合わない入力をERRORにする |
createPredicateValidator | 指定したPredicate<S>に合わない入力をERRORにする |
Predicate<S>
はコントロールのvalueを受け取ってboolean
を返す関数型インタフェースです。好きな条件をラムダ式を使って実装できるのですっきり書けますし応用が利きます。また、引数にはエラーレベル(Severity.ERROR
かSeverity.WARNING
)やエラーメッセージを指定します。
Validator (ControlsFX): API Document
複数のバリデーターを一つのコントロールにセットするにはValidator<T>::combine
を使ってバリデーターを合成する必要がありますがここでは触れません。(「~の形式で入力してください」を空チェックの兼用とするなど、一つで済ませる方法もあります)
バリデーションのコード例は次で触れます。
ValidationSupport
Valicator
はValidationSupport
クラスに登録して使用します。あとは入力内容が変更される度に自動的にバリデーションが行われるので、以下の例だけでバリデーションが全て機能します。
バリデーターの登録にはValidationSupport::registerValidator
を使用します。登録時に入力必須アイコンの表示可否を指定できます。
ValidationSupport support = new ValidationSupport();
support.registerValidator(textField1, false, Validator.createEmptyValidator(
"Text is required",
Severity.WARNING));
support.registerValidator(textField2, Validator.createEqualsValidator(
"A or B or C is required",
Arrays.asList("A", "B", "C")));
support.registerValidator(textField3, Validator.createRegexValidator(
"Number is required",
Pattern.compile("^[0-9]+$"), Severity.ERROR));
support.registerValidator(checkBox, Validator.createEqualsValidator(
"Check is required",
Collections.singletonList(true)));
support.registerValidator(comboBox, Validator.createEqualsValidator(
"First item is required",
Collections.singletonList(comboBox.getItems().get(0))));
support.registerValidator(colorPicker, Validator.createEqualsValidator(
"WHITE or BLACK is required",
Arrays.asList(Color.WHITE, Color.BLACK)));
support.registerValidator(slider, Validator.createPredicateValidator(
(Double value) -> value > 0.0,
"Positive number is required"));
// スタティックインポートを使用して
// import static org.controlsfx.validation.Validator.*;
// とすると毎回 Validator. を書かなくて済みます
ValidationSupport
クラスはバリデーション結果全体に関するプロパティを持っているので、結果全体に追従する仕組みを実装するのに便利です。
// 全ての結果がvalidな場合だけ有効なボタン
Button buttonValidated= new Button();
buttonValidated.disableProperty().bind(support.invalidProperty());
// エラーメッセージを全て表示するListView
ListView<ValidationMessage> listView = new ListView<>();
support.validationResultProperty().addListener((o, oldValue, newValue) ->
listView.getItems().setAll(newValue.getMessages()));
ValidationSupport (ControlsFX): API Document
CSSによってエラー表示を変更したい場合
上記の例はデフォルトアイコンによるエラー表示のみ行っていますがこれを変更できます。StyleClassValidateionDecoration
を使用すると警告時にはwarning
、エラー時にはerror
というスタイルクラスをコントロールに追加してくれます。実装者はScene
に適用するCSSファイルにエラー時のスタイルを記述する必要があります。アイコン表示との併用もできます。
// CSSのみの場合
support.setValidationDecorator(new StyleClassValidationDecoration());
// アイコンとCSSを併用する場合
support.setValidationDecorator(new CompoundValidationDecoration(
new GraphicValidationDecoration(),
new StyleClassValidationDecoration()));
.warning{
-fx-border-color: orange;
-fx-border-width: 2;
}
.error{
-fx-border-color: red;
-fx-border-width: 2;
}
scene.getStylesheets().addAll(getClass().getResource("Main.css").toExternalForm());
以下はアイコンとCSSを併用する場合の画面表示です。
CSSのみの場合ツールチップを表示する方法がないのでエラー内容を伝える工夫が必要です。
アイコンやツールチップをデザイン変更したい場合
アイコンやツールチップのデザイン変更は、GraphicValidationDecoration
を継承して対応するメソッドをオーバーライドする方法があります。
GraphicValidationDecoration:: | 説明 |
---|---|
createErrorNode | エラーアイコン |
createWarningNode | 警告アイコン |
createRequireDecorations | 入力必須アイコン |
createTooltip | ツールチップ |
オリジナルのソースを参考にして一部書き換える感じでやると簡単かと思います。
GraphicValidationDecoration (ControlsFX): API Document
bitbucket: GraphicValidationDecoration
support.setValidationDecorator(new YourGraphicValidationDecoration());
カスタムコントロールをバリデーションするときの注意
ControlsFXのバリデーターはあなたが作成したカスタムコントロールのどのプロパティをチェックするべきかを知りません。JavaFX標準ではないコントロールをバリデーションする場合は次のようなコードで教えてあげる必要があります。公式の説明はこちら
// YourControlコントロールについて、someプロパティの値をバリデーションして欲しいとき
ValueExtractor.addObservableValueExtractor(
c -> c instanceof YourControl,
c -> ((YourControl)c).someProperty());