JavaFXとCSS¶
CSSファイル読み込みの設定¶
FXMLからCSSファイルを指定¶
Scene Builderで画面全体に設定するCSSファイルを指定する¶
直接JavaコードでCSSを適用する場合は、Sceneを指定できます。
Scene Builderで画面を作成している場合で、Scene Builder上でCSSを画面全体に適用するには、シーングラフの階層のトップに近いPaneに適用します。
まず、シーングラフの階層paneでもっともトップにあるpaneを選択します。画面を次に示します。
次にそのpaneのプロパティでStylesheetsに適用したいCSSファイルを設定します。プロパティのStylesheets欄の[+]となっている欄(次の画面)をクリックするとファイル選択ダイアログが表示されます。
ここで、予め作成しておいたCSSファイル(次の画面では darktheme.css)を選択します。
SceneBuilderはこの時点で指定したCSSの設定に合わせて画面が表示されます。
まずCSS指定前のデフォルト(JavaFX 8の場合はmodena)の画面例を次に示します。
CSS指定後の画面例を次に示します。
SceneBuilderからCSSファイルを指定した結果、FXMLファイルには次の記述が追加されました。
<BorderPane maxHeight="-Infinity" ...
stylesheets="@darktheme.css" ...>
- BorderPaneの属性stylesheetsにファイル名が追記されています。なお、このcssファイルはFXMLファイルおよびそれを使用するソースファイルと同じディレクトリに置いています。
- 画面上部の背景が白のままですが、それは別の項で述べます。
ソースコード上でAPIでCSSファイルを指定¶
シーングラフの階層のNode(ここではScene)にCSSファイルを指定します。
Scene scene = new Scene(root);
scene.getStylesheets().add(
getClass().getResource("darktheme.css").toExternalForm()
);
FXMLでCSSファイルを指定した後でコードからCSSファイルを読み込む¶
先の指定でFXMLでCSSファイルを指定すると、クラスファイルと同じ場所にあるCSSファイルを読み込みます。
これを、プログラム実行後に別なCSSファイルを読み込み反映する場合、上述のSceneにCSSを指定してもFXMLで指定したCSSの内容を変更することができません。
Sceneに設定したCSSよりも、Sceneの子ノード(先の例ではBorderPane)に指定したCSSが優先されるためです。
そこで、CSSファイル指定を含むFXMLで定義したレイアウトをSceneに設定している場合、別なCSSファイルをプログラムコードから指定するには、次のようにSceneからrootノード(FXMLでロードしたトップノード)を取得し、そこにスタイルシートを設定します。
File file = ...
scene.getRoot().getStylesheets().add(
file.toURI().toExternalForm()
);
CSSの書き方¶
基本的な記法¶
CSSの基本的な記法は次です。JavaFX CSSもこれに従います。
<セレクター> { 属性名 : 属性値 ; 属性名 : 属性値 ; }
セレクター¶
CSSでは、セレクターは(HTMLの)要素名、または要素に指定したクラス属性/ID属性です。疑似クラス(hover等)も使えます。
JavaFXでは、セレクターの指定にはCSSにおけるクラス属性およびID属性を使用します。JavaFXのドキュメントでは、CSSのクラス属性を「スタイル・クラス」と呼んでいます。
CSSのルールでは、クラス属性を指定するときはクラス属性名の先頭にピリオド(.)を、ID属性を指定するときはID属性名の先頭にナンバーサイン(#)を付けます。
JavaFXの標準コントロールには、スタイル・クラス(クラス属性)が設定済みなので、そのコントロールすべてにCSSの設定を適用する場合は、設定済みのスタイル・クラスをセレクターに指定します。例えば、Buttonクラスにはクラス属性 "button" が設定されているので、セレクターには ".button"を指定します。
例)すべてのJavaFX標準ボタンの背景色を赤に設定する
.button {
-fx-background-color: red;
}
JavaFXのレイアウト部品には、スタイル・クラス(クラス属性)が未設定なので、レイアウト部品にCSSの設定を適用する場合は、レイアウト部品にスタイル・クラスを指定するか、後述のID属性を指定する必要があります。
独自のスタイル・クラス¶
独自のスタイル・クラスを個々のインスタンスに設定するには、getStyleClass().add(スタイル・クラス名)
を使用します。
例)キャンセルボタンの文字色を指定
Javaコードでキャンセルボタンのインスタンスにスタイル・クラスを設定
@FXML Button cancelButton;
:
cancelButton.getStyleClass().add("cancel-button");
CSSファイルでキャンセルボタンに文字色を設定
.cancel-button {
-fx-text-fill: #006464;
}
どのコントロールにどんなスタイル・クラスが付けられているかは、「JavaFX CSSリファレンス・ガイド2」 に記載されています。なお、レイアウト部品系にはスタイル・クラスの設定がありません。リファレンス・ガイドに、「スタイル・クラス: デフォルトでは空」と記載されている部品がそうです。
このあたりを以前はてな日記に記載しました。
JavaFX 8でcssファイルに定義したHBoxのスタイルが反映されない
画面の背景をCSSで一括設定するには
独自のID¶
JavaFXの部品インスタンスには、setId(ID名)
でID属性を設定できます。
例)id属性alfaを指定したボタンの文字色を指定
JavaコードでボタンのIDを設定
@FXML Button alfaButton;
:
alfaButton.setId("alfa");
CSSファイルでalfaボタンに文字色を設定
#alfa {
-fx-text-fill: #802020;
}
階層指定¶
シーングラフ上であるノードの下にあるノードだけを限定的に指定することが可能です。
.some-pane .button {
-fx-text-fill: red;
}
.some-pane .button
のように空白で区切ってセレクターを並べた場合、最初のセレクターで指定されるノード以下の階層のどこかに次のセレクターが存在するときだけ適用されます。.some-pane > .button
のように、">"で区切ってセレクターを並べた場合、最初のセレクターで指定されるノードの直下に次のセレクターが存在するときだけ適用されます。
擬似クラス¶
ラベルを非活性化(disabled)したときの見栄えは、JavaFXのmodenaでは次の様に記述されています。(分かりやすいようラベルのセレクタのみ抽出)
.label:disabled {
-fx-opacity: 0.4;
}
ここで、セレクタに:disabled
とあるのが擬似クラスとなります。セレクタのクラスがある状態を取るときの定義で、非活性化の他、マウスが部品の上に重なっている間を表す:hover
などがあります。
独自に擬似クラスを定義したい¶
非活性化とは異なり部品に操作は可能としたいが、ある状態の間は薄く表示させたいとします。
この状態を:haze
という擬似クラスで定義するとします。
まず、CSSにラベルの擬似クラス:haze
を定義し薄くするため透過度を定義します。
.label:haze {
-fx-opacity: 0.2;
}
次に、ラベルの状態を切り替える際に擬似クラスを設定します。
トグルボタンが押されているとき(isSelected が true)のときにラベルを:haze
状態とする場合を
次のコードに示します。
private static final PseudoClass HAZE_PSEUDO_CLASS = PseudoClass.getPseudoClass("haze");
@FXML private ToggleButton toggle;
@FXML private Label label;
:
public void initialize(URL location, ResourceBundle resources) {
toggle.selectedProperty().addListener((obs, ov, nv) ->
label.pseudoClassStateChanged(HAZE_PSEUDO_CLASS, nv));
:
CSSの設定例¶
グラデーションの設定¶
放射状グラデーション¶
背景を塗りつぶす場合は、-fx-background-color
、-fx-background-radius
、-fx-background-insets
を使います。
今回は、Paneをそのままグラデーションで塗りつぶすので、-fx-background-color
を指定します。
#myPane {
-fx-background-color:
radial-gradient(center 50% 50%, radius 50%, blue, green 50%, blue 80%, green 95%, black);
}
ポップアップメニューの設定¶
Modenaの設定¶
Java SE 8のデフォルトテーマ(Modena)で表示されるポップアップメニューは次です。
ポップアップメニューの下敷きはContextMenuクラスなので、modenaの設定を調べると次のように定義されています。
.context-menu {
-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-padding: 0.333333em 0.083333em 0.333333em 0.083333em; /* 4 1 8 1 */
-fx-effect: dropshadow( gaussian , rgba(0,0,0,0.2) , 12, 0.0 , 0 , 8 );
}
- -fx-color の設定は次のようになっています(明るい灰色)
-fx-base: #ececec; -fx-color: -fx-base;
ポップアップメニューの各メニュー項目はMenuItemクラスなので、modenaの設定を調べると次のように定義されています。
.menu-item {
-fx-background-color: transparent;
-fx-padding: 0.333333em 0.41777em 0.333333em 0.41777em; /* 4 5 4 5 */
}
.menu-item > .left-container {
-fx-padding: 0.458em 0.791em 0.458em 0.458em;
}
.menu-item > .graphic-container {
-fx-padding: 0em 0.333em 0em 0em;
}
.menu-item >.label {
-fx-padding: 0em 0.5em 0em 0em;
-fx-text-fill: -fx-text-base-color;
}
.menu-item:focused {
-fx-background: -fx-selection-bar;
-fx-background-color: -fx-background;
-fx-text-fill: -fx-text-background-color;
}
.menu-item:focused > .label {
-fx-text-fill: -fx-focused-text-base-color;
}
.menu-item > .right-container {
-fx-padding: 0em 0em 0em 0.5em;
}
.menu-item:show-mnemonics > .mnemonic-underline {
-fx-stroke: -fx-text-fill;
}
メニューアイテムは透過なので、背景のポップアップの色となります。
メニューアイテムの文字は、通常時、フォーカス時に別な色が設定されています。
通常時のテキスト色は次に示すようにCSSのladder関数で定義されます。
-fx-text-base-color: ladder(
-fx-color,
-fx-light-text-color 45%,
-fx-dark-text-color 46%,
-fx-dark-text-color 59%,
-fx-mid-text-color 60%
);
- -fx-color(=-fx-base=#ececec)は、明度92なので、-fx-text-base-colorの値は-fx-mid-text-color(
#333
)になります。
ladder関数は、第1引数の色の明度に基づき、第2引数以降の任意個数のストップから構成されるグラディエーションの中から1つの値を決定します。明度が0%だとグラデーションの0.0側、明度が100%だとグラデーションの1.0側が使われます。
フォーカス時のテキスト色
-fx-focused-text-base-color : ladder(
-fx-selection-bar,
-fx-light-text-color 45%,
-fx-dark-text-color 46%,
-fx-dark-text-color 59%,
-fx-mid-text-color 60%
);
- -fx-selection-bar(=-fx-accent=#0096C9)は明度78なので、-fx-focused-text-base-colorの値は-fx-mid-text-color(#333)となります。
- テキストの色指定で使われる個々の色定義
-fx-dark-text-color: black; -fx-mid-text-color: #333; -fx-light-text-color: white;
メニューアイテム¶
メニューアイテム(MenuItemクラス)のCSS定義でフォントサイズを変更します。
.menu-item {
-fx-font-size: 20;
}
スライダーの目盛りラベル¶
スライダー(Sliderクラス)で目盛りラベルのフォントサイズを変更します。
JavaFX CSSリファンレンスのSliderの項を見ても、目盛りラベルのプロパティは見当たりません。そこで、Slider項のサブストラクチャとしてaxis(目盛り)があるのを見つけ、Axis項のプロパティを調べます。Axis項はチャートに分類されていますが、SliderもAxisを使用しています。
.slider .axis {
-fx-tick-label-font-size: 1em;
}
スライダーの目盛りだけを指定するため、セレクターにはSliderの階層下にあるAxisを指定しています。
ツール¶
Scene Builder¶
Scene Builderには、CSS Analyzer機能があります。「View]メニュー > [Show CSS Analyzer]をクリックします。
すると、画面下部に選択した部品に適用されるCSSプロパティの一覧が表示されます。
上述は、Sliderの目盛りを調べているところです。
コード¶
CSS(外部ファイル)で適用したスタイルをコードで取得¶
CSSで指定したスタイルの情報は、個々のコントロールからは取得できません。そこで、ルートノード等からセレクターを指定して取得します。
text3.getParent().lookup("#meiryoui").getCssMetaData().stream()
.filter(p->p.getProperty().equals("-fx-font-family"))
.findFirst()
.ifPresent(System.out::println);
- AnchorPaneにスタイルシートファイルを設定し、text3がAnchorPaneに貼られている場合のコード
参考文献¶
オラクル公式ドキュメント¶
1 JavaFX: JavaFX UIコンポーネントの操作 37 CSSによるUIコントロールのスタイル設定(Java SE 8ドキュメントの一部)
http://docs.oracle.com/javase/jp/8/javafx/user-interface-tutorial/apply-css.htm
2 JavaFX CSS リファレンス・ガイド(Java SE 8ドキュメントの一部)
http://docs.oracle.com/javase/jp/8/javafx/api/javafx/scene/doc-files/cssref.html