2014年3月8日 星期六

JavaFX CSS Styling

JavaFX支援CSS樣式 (Cascading Style Sheets Styling),以CSS設定物件樣式,JavaFX程式在執行階段只需讀取CSS檔案,則可設定JavaFX中各物件的樣式,一旦樣式變更,也僅需修改CSS檔案,不需修改JavaFX程式,對於熟悉CSS語法的使用者而言,是一個不錯的選擇。

JavaFX定義各類別的CSS樣式,各樣式之屬性可參考JavaFX CSS Reference Guide

以Layout Pane為例,JavaFX分別定義以下各Layout Pane類別的CSS樣式屬性。

FlowPane類別:
  • -fx-alignment:設定Flow Pane水平與垂直對齊方式。
  • -fx-orientation:設定Flow Pane的配置方向。
  • -fx-hgap:設定Flow Pane物件間的水平間距。
  • -fx-vgap:設定Flow Pane物件間的垂直間距。
  • -fx-column-halignment:當為垂直配置時,設定其直行中各物件的水平對齊方式。
  • -fx-row-valignment:當為水平配置時,設定其橫列中各物件的垂直對齊方式。
GridPane類別:
  • -fx-alignment:設定Grid Pane水平與垂直對齊方式。
  • -fx-hgap:設定Grid Pane物件間的水平間距。
  • -fx-vgap:設定Grid Pane物件間的垂直間距。
  • -fx-grid-lines-visible:設定是否顯示Grid Pane的格線。
HBox類別:
  • -fx-alignment:設定HBox水平與垂直對齊方式。
  • -fx-fill-height:設定是否調整各物件的高度為其最佳高度。
  • -fx-spacing:設定HBox中各物件間的水平間距。
StackPane類別:
  • -fx-alignment:設定Stack Pane水平與垂直對齊方式。
TilePane類別:
  • -fx-alignment:設定Tile Pane水平與垂直對齊方式。
  • -fx-orientation:設定Tile Pane的配置方向。
  • -fx-hgap:設定Tile Pane物件間的水平間距。
  • -fx-vgap:設定Tile Pane物件間的垂直間距。
  • -fx-pref-columns:設定Tile Pane最佳直行數目。
  • -fx-pref-rows:設定Tile Pane最佳橫列數目。
  • -fx-pref-tile-height:設定每一Tile的最佳高度。
  • -fx-pref-tile-width:設定每一Tile的最佳寬度。
VBox類別:
  • -fx-alignment:設定VBox水平與垂直對齊方式。
  • -fx-fill-width:設定是否調整各物件的寬度為其最佳寬度。
  • -fx-spacing:設定VBox中各物件間的垂直間距。
以Layout Pane為例,欲使用CSS樣式,有以下兩種方式。

第一種方式是將樣式定義於CSS檔案中,以CSS檔案定義樣式的優點在於,JavaFX程式在執行階段只需讀取CSS檔案,則可設定JavaFX中各物件的樣式,一旦樣式變更,僅需修改CSS檔案,不需修改JavaFX程式。

請參考以下範例示範以CSS檔案設定Border Pane與背景樣式,CSS檔案 (LayoutPane.css) 的內容如下,可將此檔案與JavaFX程式檔案置於同一目錄中。由於JavaFX並沒有定義Border Pane的CSS樣式,因此在CSS檔案中沿用Region類別的CSS樣式,分別設定背景顏色、背景圖像、是否重複顯示背景圖像、背景圖像位置等,其中pane在CSS語法中稱為CSS選取器 (CSS Selector):


.pane {
  -fx-background-color: #abffff;
  -fx-background-image: url("images/javafx.jpg");
  -fx-background-repeat: no-repeat;
  -fx-background-position: center;
}


待完成CSS檔案設定之後,在JavaFX程式中,以下列方式讀取CSS檔案內容,則完成CSS樣式的設定。首先以Scene類別的getStylesheets().add()方法讀取CSS檔案,以上述範例為例,由於CSS檔案與JavaFX程式檔案置於同一目錄之中,因此其讀取方式如下,其中borderpanedemo代表套件 (Package) 名稱:


// 設定Scene的Layout Pane為BorderPane
Scene scene = new Scene(borderpane);

// 讀取CSS檔案內容
scene.getStylesheets().add("borderpanedemo/LayoutPane.css");
...


接著以BorderPane類別的getStyleClass().add()getStyleClass().addAll()方法,設定其CSS樣式為CSS檔案中所定義的pane選取器:


BorderPane borderpane = new BorderPane();

// 設定標籤的CSS樣式為CSS檔案中的pane選取器
borderpane.getStyleClass().add("pane");
...


如此則完成CSS樣式的設定。

【執行結果】
第二種方式與第一種方式相似,同樣是將標籤樣式定義於CSS檔案中,其差異只在於讀取CSS檔案容的方式不同。請參考以下範例示範以CSS檔案設定Flow Pane與背景樣式,CSS檔案(LayoutPane.css)的內容如下,分別設定paneflowpane選取器:


.pane {
  -fx-background-color: #abffff;
  -fx-background-image: url("images/javafx.jpg");
  -fx-background-repeat: no-repeat;
  -fx-background-position: center;
}

.flowpane {
  -fx-padding: 5.0 5.0 5.0 5.0;
  -fx-hgap: 5;
  -fx-vgap: 5;
  -fx-alignment: center;
}


待完成CSS檔案設定之後,在JavaFX程式中,以下列方式讀取CSS檔案內容,則完成CSS樣式的設定,所不同的是其讀取CSS檔案內容的方式,同樣以Scene類別的getStylesheets().add()方法讀取CSS檔案,但其內容與前述範例稍有不同,其中FlowPaneDemo代表範例之類別名稱:


// 設定Scene的Layout Pane為FlowPane
Scene scene = new Scene(flowpane);

// 讀取CSS檔案內容
scene.getStylesheets().add(
  FlowPaneDemo.class.getResource("LayoutPane.css").toExternalForm());
...


或是以下的方式:


// 設定Scene的Layout Pane為FlowPane
Scene scene = new Scene(flowpane);

// 讀取CSS檔案內容
scene.getStylesheets().add(
  getClass().getResource("LayoutPane.css").toExternalForm());
...


由於CSS檔案分別設定paneflowpane選取器,因此接著以FlowPane類別的getStyleClass().addAll()方法,設定其CSS樣式為CSS檔案中所定義的paneflowpane選取器:


FlowPane flowpane = new FlowPane();

// 設定標籤的CSS樣式為CSS檔案中的pane與flowpane選取器
flowpane.getStyleClass().addAll("pane", "flowpane");
...


如此則完成CSS樣式的設定。

【執行結果】
此外,針對GUI物件,則有以下處理CSS樣式的方法。

以標籤 (Label) 為例,JavaFX定義以下有關標籤的CSS樣式:
  • -fx-alignment:設定標籤水平與垂直的對齊方式。
  • -fx-content-display:設定圖像與文字間的相對位置。
  • -fx-ellipsis-string:設定標籤文字的省略符號。
  • -fx-font:設定文字字型。
  • -fx-font-family:設定文字字體。
  • -fx-font-size:設定文字大小。
  • -fx-font-style:設定文字樣式。
  • -fx-font-weight:設定字體粗細。
  • -fx-graphic:設定標籤所使用的圖像。
  • -fx-graphic-text-gap:設定圖像與文字間的間距。
  • -fx-label-padding:設定標籤四周的間距。
  • -fx-text-alignment:設定標籤文字的對齊方式。
  • -fx-text-fill:設定標籤文字的顏色。
  • -fx-underline:是否設定底線。
  • -fx-wrap-text:設定當標籤文字長度超過Layout Pane的寬度時,是否自動換行。
欲使用CSS樣式,除了以上述CSS檔案處理之外,亦可直接在程式中,以Node抽象類別的setStyle()方法設定,將所有的CSS樣式以分號隔開,例如:


Label label1 = new Label("...");

// 以分號隔開所有的CSS樣式
label1.setStyle("-fx-font-family: Verdana;
  -fx-font-size: 10px; -fx-font-style: italic;
  -fx-font-weight: bold; -fx-text-alignment: left;
  -fx-text-fill: #FF0000; -fx-wrap-text: true;");
...


【執行結果】
第二種方式是將CSS樣式定義於CSS檔案中,以CSS檔案定義樣式的優點在於,JavaFX程式在執行階段只需讀取CSS檔案,則可設定JavaFX中各物件的樣式,且一旦樣式變更,也僅需修改CSS檔案,不需修改JavaFX程式。

請參考以下範例示範以CSS檔案設定標籤的樣式,CSS檔案 (Label.css) 的內容如下,可將此檔案與JavaFX程式檔案置於同一目錄中。其中rootlabel在CSS語法中稱為CSS選取器 (CSS Selector):


root {
  display: block;
}

.root {
  -fx-background-image: url("images/javafx.jpg");
}

.label {
  -fx-font-family: Verdana;
  -fx-font-size: 10px;
  -fx-font-style: italic;
  -fx-font-weight: bold;
  -fx-text-alignment: left;
  -fx-text-fill: #FF0000;
  -fx-wrap-text: true;
  -fx-graphic: url("images/dukeswing.gif");
}


上述設定除了示範如何設定標籤的相關文字樣式之外,並示範如何設定背景與標籤所使用的圖像。待完成CSS檔案設定之後,在JavaFX程式中,以下列方式讀取CSS檔案內容,則完成CSS樣式的設定。首先以Scene類別的getStylesheets().add()方法讀取CSS檔案,其中labeldemo代表範例之套件 (Package) 名稱:


// 設定Scene的Layout Pane為HBox
Scene scene = new Scene(hbox);

// 讀取CSS檔案內容
scene.getStylesheets().add("labeldemo/Label.css");
...


接著以Label類別的getStyleClass().add()getStyleClass().addAll()方法,設定標籤的CSS樣式為CSS檔案中所定義的rootlabel選取器 (此段程式亦可省略):


Label label1 = new Label("...");

// 設定標籤的CSS樣式為CSS檔案中的root與label選取器
label1.getStyleClass().addAll("root", "label");
...


【執行結果】
【參考資料】

[1] 黃嘉輝,深入研究JavaFX 2。
[2] 黃嘉輝,深入研究Java Swing。
[3] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[4] JavaFX:http://www.oracle.com/technetwork/java/javafx
[5] JavaFX 2.2 API Specification.
[6] Java Platform, Standard Edition 7 API Specification.
[7] JavaFX CSS Reference Guide.

© Chia-Hui Huang

2 則留言:

  1. 請問一下最後一段"接著以Label類別的getStyleClass().add()或getStyleClass().addAll()方法,設定標籤的CSS樣式為CSS檔案中所定義的root與label選取器 (此段程式亦可省略):" 為何label1.getStyleClass().addAll("root", "label"); 可以省略? 一般不是要設定 node 和 CSS selector 對應關係不是嗎? "深入研究 JavaFX 2" 一書中Example 4-16 和 Example 3-24 使用差別是在哪裡?

    回覆刪除
    回覆
    1. Dear uddhava您好

      謝謝你的詢問,Example 4-16 和 Example 3-24 並無差別,差別在於兩者CSS的設定,Example 4-16 分別設定.root 和.text,因此程式中使用text1.getStyleClass().addAll("root", "text");,Example 3-24分別設定.pane 和.flowpane,因此程式中使用flowpane.getStyleClass().addAll("pane", "flowpane");

      至於"(此段程式亦可省略)",因為之前已經以scene.getStylesheets().add("labeldemo/Label.css");,其實這樣的設定已經足夠,所以label1.getStyleClass().addAll("root", "label");可以省略

      感謝你

      Leo

      刪除