2014年12月8日 星期一

Polyhedron (Processing)

以下是Processing建構之Polyhedron。

Cube

Octohedron

Dodecahedron

Cuboctahedron

Icosahedron

Truncated Cube

Truncated Octahedron

Rhombic Dodecahedron

Rhombic Triacontahedron

【參考資料】

[1] Open Processing:http://www.openprocessing.org/
[2] Ira Greenberg, Dianna Xu, Deepak Kumar, Processing: Creative Coding and Generative Art in Processing 2, Friends of ED.
[3] Vantomme Jan, Processing 2: Creative Programming Cookbook, Packt Publishing.

© Chia-Hui Huang

2014年12月7日 星期日

Archimedean Solid (Processing)

以下是Processing建構之Archimedean Solid (阿基米德立體) 物件,阿基米德立體是一種高度對稱的半正多面體 (Semi-regular Polyhedron),使用兩種或兩種以上的正多邊形為面,並可從正多面體經過截角、截半、截邊等操作建構半正多面體。由於阿基米德立體的面是由正多邊形所組成,每個相鄰正多邊形的邊長均相等,因此阿基米德立體的邊均為相同長度。

Examples: http://webmail.ntcb.edu.tw/~leohuang/zh-tw/processing.html

Rhombicuboctahedron

Icosidodecahedron

Truncated Cuboctahedron

Truncated Dodecahedron

Truncated Icosahedron

Small Rhombicosidodecahedron

Great Rhombicosidodecahedron

【參考資料】

[1] Open Processing:http://www.openprocessing.org/
[2] Ira Greenberg, Dianna Xu, Deepak Kumar, Processing: Creative Coding and Generative Art in Processing 2, Friends of ED.
[3] Vantomme Jan, Processing 2: Creative Programming Cookbook, Packt Publishing.

© Chia-Hui Huang

2014年12月6日 星期六

Processing

無意間發現Processing,這麼有趣簡單的程式,真是後知後覺、相見恨晚,隨便幾行就能產生有趣的動畫效果。
例如以下範例處理滑鼠事件,完全沒有感覺有處理事件,太精簡了,超厲害:
void setup() {
  size(640, 480);
  background(255);
  smooth();
}

void draw() {
  ellipse(mouseX, mouseY, 80, 80);
}
【執行結果】
更不用說複雜的Fractal Tree,也是不到20行就搞定。

Examples: http://webmail.ntcb.edu.tw/~leohuang/zh-tw/processing.html

【參考資料】

[1] Open Processing:http://www.openprocessing.org/
[2] Ira Greenberg, Dianna Xu, Deepak Kumar, Processing: Creative Coding and Generative Art in Processing 2, Friends of ED.
[3] Vantomme Jan, Processing 2: Creative Programming Cookbook, Packt Publishing.

© Chia-Hui Huang

2014年12月3日 星期三

JavaFX Cell Factory (1) ComboBox與ListView

今天刊登於CodeData網站,有關JavaFX的ComboBox與ListView如何設定Cell Factory,將每一個選項項目變更為其他物件如文字字串、文字欄位或複合方塊等物件,以改變每一個選項項目的形式:

http://www.codedata.com.tw/java/javafx-cell-factory-1-combox-listview/

© Chia-Hui Huang

2014年12月1日 星期一

JavaFX 8技術手冊

JavaFX 8技術手冊的封面終於出來了,真是漂亮,感謝編輯創意,封面概念借用caterpillar林信良所著之Java SE 8 技術手冊,但為了區隔,所以底色為黑色。
© Chia-Hui Huang

2014年11月29日 星期六

JavaFX Fractal Tree

【參考資料】

[1] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[2] JavaFX:http://www.oracle.com/technetwork/java/javafx
[3] JavaFX 8.0 API Specification.
[4] Java Platform, Standard Edition 8 API Specification.

© Chia-Hui Huang

JavaFX Fractal Tree

【參考資料】

[1] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[2] JavaFX:http://www.oracle.com/technetwork/java/javafx
[3] JavaFX 8.0 API Specification.
[4] Java Platform, Standard Edition 8 API Specification.

© Chia-Hui Huang

2014年11月28日 星期五

JavaFX Barnsley Fern

【參考資料】

[1] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[2] JavaFX:http://www.oracle.com/technetwork/java/javafx
[3] JavaFX 8.0 API Specification.
[4] Java Platform, Standard Edition 8 API Specification.

© Chia-Hui Huang

JavaFX Fractal Tree

【參考資料】

[1] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[2] JavaFX:http://www.oracle.com/technetwork/java/javafx
[3] JavaFX 8.0 API Specification.
[4] Java Platform, Standard Edition 8 API Specification.

© Chia-Hui Huang

JavaFX Julia Set


【參考資料】

[1] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[2] JavaFX:http://www.oracle.com/technetwork/java/javafx
[3] JavaFX 8.0 API Specification.
[4] Java Platform, Standard Edition 8 API Specification.

© Chia-Hui Huang

JavaFX Koch Snowflakes

【參考資料】

[1] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[2] JavaFX:http://www.oracle.com/technetwork/java/javafx
[3] JavaFX 8.0 API Specification.
[4] Java Platform, Standard Edition 8 API Specification.

© Chia-Hui Huang

2014年11月27日 星期四

JavaFX Fractal Tree


【參考資料】

[1] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[2] JavaFX:http://www.oracle.com/technetwork/java/javafx
[3] JavaFX 8.0 API Specification.
[4] Java Platform, Standard Edition 8 API Specification.

© Chia-Hui Huang

2014年11月24日 星期一

JavaFX 8技術手冊

完成JavaFX 8技術手冊第五校,就等封面完成就可以出版了,章節包括:

1. JavaFX
2. JavaFX程式架構
3. Layout Pane
4. 效果
5. 事件
6. 按鈕
7. 選項項目
8. 捲軸、滑動軸、進度指示器與進度列
9. 選單
10. 文字編輯物件
11. 窗格
12. 對話盒
13. Web
14. 樹
15. 表格
16. 樹狀表格
17. 繪圖
18. 動畫
19. 圖表
20. 多媒體
21. 3D

© Chia-Hui Huang

2014年11月21日 星期五

JavaFX 8技術手冊

一天之內完成JavaFX 8技術手冊的第三與第四校,真是瘋狂賣命,快結束了。

© Chia-Hui Huang

2014年11月16日 星期日

JavaFX 8技術手冊

今天完成JavaFX 8技術手冊的第二校,寫書不難,校稿才是最辛苦的事。

自2000年開始寫第一本書至今,已出了繁體和簡體書共40本,賣得好不好是一回事,看著自己辛苦的成果放在書架上,就已經很滿足了。今年終於因為健康的關係,不得不忍痛停止。


© Chia-Hui Huang

2014年11月11日 星期二

Fisheye Picture Menu (JavaFX)

Fisheye Picture Menu (魚眼式圖片功能表) 是商業智慧軟體所提供的功能之一,類似MacOS的Dock功能表,功能表包含數個圖片,當滑鼠靠近圖片時,圖片會自動放大,以產生動態更改大小的效果。透過魚眼式圖片功能表可將資料依不同的條件篩選出不同的結果,以建立圖表互動,讓圖表更加多樣化。

此次以JavaFX開發Fisheye Picture Menu,以滑鼠事件處理當滑鼠移至圖片上方時,則放大圖片比例並顯示相對應的圖表。當滑鼠離開圖片時,則恢復圖片正常比例。

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

[1] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[2] JavaFX:http://www.oracle.com/technetwork/java/javafx
[3] JavaFX 8.0 API Specification.
[4] Java Platform, Standard Edition 8 API Specification.

© Chia-Hui Huang

JavaFX特效 (2) 漸層 (Gradient) 處理

今天刊登於CodeData網站,有關JavaFX特效之漸層 (Gradient) 處理:

http://www.codedata.com.tw/java/javafx-effect-2-gradient/

© Chia-Hui Huang

2014年11月4日 星期二

JavaFX Mandelbrot Set

Mandelbrot Set (曼德布洛特集) 是一種在複平面上組成分形之點集合,以數學家Benoit B. Mandelbrot為名。

以前曾以Java Swing寫過一個Mandelbrot Set程式,這次試著改以JavaFX,還有很多地方需要修改。
Mandelbrot Set (Java Swing)

Mandelbrot Set (JavaFX)

【參考資料】

[1] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[2] JavaFX:http://www.oracle.com/technetwork/java/javafx
[3] JavaFX 8.0 API Specification.
[4] Java Platform, Standard Edition 8 API Specification.
[5] Mandelbrot Set: http://en.wikipedia.org/wiki/Mandelbrot_set.

© Chia-Hui Huang

如何在JavaFX中開啟新視窗?

JavaFX應用程式需繼承javafx.application.Application抽象類別,Application抽象類別類似於Java Swing的JApplet類別,以其「生命週期」執行JavaFX,分別為:
  • init():初始化JavaFX應用程式。
  • start():為JavaFX應用程式的進入點,在此為Java Applet。
  • launch():啟動獨立 (Standalone) 的JavaFX應用程式,在此為視窗程式。
  • stop():當JavaFX應用程式結束之前,將執行stop()方法。
其程式架構如下:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
...

// 繼承javafx.application.Application抽象類別
public class JavaFXHelloWorld extends Application {

  @Override
  public void start(Stage primaryStage) {
    ...
  }

  public static void main(String[] args) {
    launch(args);
  }
}
其中javafx.stage.Stage類別為JavaFX最上層的容器 (Container),類似於Java Swing的JRootPane類別,用以置放SceneSubScene物件。javafx.scene.Scene類別則是JavaFX Scene Graph,可藉以繪圖或配置各類GUI物件。

因此欲在原視窗中產生另一個新視窗,只要使用Stage類別則可,以下範例示範當點選原視窗中的按鈕時,則產生另一個新視窗:
public class StageDemo extends Application {

  @Override
  public void start(Stage primaryStage) {
    
    // Normal Button
    Button button = new Button();
    button.setText("New Window");
    button.setPrefSize(150, 20);
    
    button.setOnAction(event -> {
      newWindow();
    });
    
    BorderPane borderpane = new BorderPane();
    borderpane.setCenter(button);
    
    // Set the Layout Pane of Scene
    Scene scene = new Scene(borderpane);    
    // Set the title of Stage
    primaryStage.setTitle("Stage Demo");
    // Set the width of Stage
    primaryStage.setWidth(250);
    // Set the height of Stage
    primaryStage.setHeight(150);
    primaryStage.setScene(scene);
    primaryStage.setResizable(false);
    
    // Show Stage
    primaryStage.show();
  }
  
  private void newWindow(){
    Label label1 = new Label("Name:");
    Label label2 = new Label("Birthday:");
    Label label3 = new Label("Tel.:");
    
    TextField textfield1 = new TextField();
    textfield1.setText("Athena");
    textfield1.setPrefColumnCount(15);
    textfield1.selectAll();
    textfield1.setTooltip(new Tooltip("Name"));
    
    TextField textfield2 = new TextField();
    textfield2.setPrefColumnCount(15);
    textfield2.setAlignment(Pos.CENTER_RIGHT);
    textfield2.setCursor(Cursor.CROSSHAIR);
    textfield2.setTooltip(new Tooltip("Birthday"));

    TextField textfield3 = new TextField();
    textfield3.setText("1234567890");
    textfield3.setPrefColumnCount(15);
    textfield3.setAlignment(Pos.CENTER);
    textfield3.setCursor(Cursor.HAND);
    textfield3.selectAll();
    textfield3.setTooltip(new Tooltip("Phone Number"));
           
    GridPane gridpane = new GridPane();
    gridpane.setAlignment(Pos.CENTER);
    gridpane.setHgap(10);
    gridpane.setVgap(10);
    gridpane.setPadding(new Insets(5, 5, 5, 5));

    gridpane.add(label1, 0, 0);
    gridpane.add(label2, 0, 1);
    gridpane.add(label3, 0, 2);

    gridpane.add(textfield1, 1, 0);
    gridpane.add(textfield2, 1, 1);
    gridpane.add(textfield3, 1, 2);

    // Set the Layout Pane of Scene
    Scene scene = new Scene(gridpane);
    
    Stage stage = new Stage();

    // Set the title of Stage
    stage.setTitle("New Window");
    // Set the width of Stage
    stage.setWidth(300);
    // Set the height of Stage
    stage.setHeight(150);
    stage.setScene(scene);
    stage.setResizable(false);

    // Show Stage
    stage.show();
  }

  /**
   * @param args the command line arguments
   */
  public static void main(String[] args) {
    launch(args);
  }
}
【執行結果】

當點選原視窗中的按鈕時,則產生另一個新視窗:
【參考資料】

[1] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[2] JavaFX:http://www.oracle.com/technetwork/java/javafx
[3] JavaFX 8.0 API Specification.
[4] Java Platform, Standard Edition 8 API Specification.

© Chia-Hui Huang

2014年10月24日 星期五

JavaFX 8技術手冊

今天完成JavaFX 8技術手冊的第一校,排的真不錯,約760頁,可惜是黑白印刷,無法呈現效果 (Effect) 與JavaFX 3D的色彩與美感。

期待新書出版,以下是我借用caterpillar林信良所著之Java SE 8 技術手冊所改編的封面,書商不見得會使用,但自得其樂。


© Chia-Hui Huang

JavaFX 3D (3) PhongMaterial貼圖處理

今天刊登於CodeData網站,有關JavaFX 3D的PhongMaterial貼圖處理:

http://www.codedata.com.tw/java/javafx-3d-3-phongmaterial/

© Chia-Hui Huang

2014年10月18日 星期六

JavaFX Cell Factory (4)

JavaFX 8新增樹狀表格 (Tree Table),樹狀表格結合樹與表格物件,因此樹狀表格與後兩者十分類似,特色是將樹狀物件置於表格單元之中,讓表格單元中的資料以階層的方式呈現,下圖為樹狀表格的樣式:
其中:
  • Tree Table View:樹狀表格。
  • Tree Table Header:樹狀表格標題。
  • Tree Table Column:樹狀表格直行。
  • Tree Table Row:樹狀表格橫列。
  • Tree Table Cell:樹狀表格單元。
  • Tree Item:樹狀節點。
  • Table Menu Button:表格選單按鈕。
  • Column Sorter:表格直行排序器,可針對直行排序。
  • Editable Cell:可編輯的樹狀表格單元。
  • Vertical Line:垂直格線。
  • Scrollbar:捲軸,分為水平與垂直捲軸。
樹狀表格由Tree Table View、Tree Table Column、Tree Table Row與Tree Table Cell所組成,TreeTableView類別用以置放直行、橫列與樹狀表格單元,並顯示樹狀表格,為樹狀表格的最上層,如同TableViewTreeViewComboBoxListView類別一般,可修改各樹狀表格單元的形式,並分別依據直行或橫列設定其Cell Factory。

樹狀表格單元的類別為javafx.scene.control.TreeTableCell,繼承自TreeTableCell的類別包括:
  • 核取方塊樹狀表格單元:CheckBoxTreeTableCell.
  • 選項方塊樹狀表格單元:ChoiceBoxTreeTableCell.
  • 複合方塊樹狀表格單元:ComboBoxTreeTableCell.
  • 進度列樹狀表格單元:ProgressBarTreeTableCell.
  • 文字欄位樹狀表格單元:TextFieldTreeTableCell.
因此可使用核取方塊、選項方塊、複合方塊、進度列與文字欄位等取代原樹狀表格單元,較樹狀單元多了進度列物件。

以下範例以繼承TreeTableCell類別之自定TextFieldCellCallback類別做為setCellFactory()方法的回呼函式。在TextFieldCellCallback類別的建構函式中,以setAlignment()方法設定欄位內容為置中對齊:

private final class TextFieldCellCallback 
  extends TreeTableCell<Department, String> {

  TextField textfield;

  // 建構函式
  public TextFieldCellCallback() {
    // 設定欄位內容為置中對齊
    setAlignment(Pos.CENTER);
  }
  ...
}
...
TextFieldCellCallback類別中,分別覆寫TreeTableCell類別的startEdit()cancelEdit()updateItem()方法以處理開始編輯、取消編輯與更新樹狀表格單元等。

以開始編輯為例,在覆寫startEdit()方法中,當以滑鼠點選樹狀表格單元時,則以setGraphic()方法將樹狀表格單元設定為文字欄位,以此編輯樹狀表格單元的內容。此外,並以setOnKeyReleased()方法處理當在樹狀表格單元文字欄位釋放鍵盤按鍵時之事件,若按下Enter鍵,代表完成輸入,則以commitEdit()方法完成編輯樹狀表格單元;若按下ESC鍵,代表取消輸入,則以cancelEdit()方法取消編輯樹狀表格單元:

// 覆寫TreeTableCell類別的startEdit()方法
@Override public void startEdit() {
  super.startEdit();

  if (textfield == null) {
    textfield = new TextField(getItem() == null ? "" : getItem());
    textfield.setMinWidth(this.getWidth()-this.getGraphicTextGap()*2);
    
    textfield.setOnKeyReleased((KeyEvent e) -> {
      if (e.getCode() == KeyCode.ENTER) {
        // 完成編輯樹狀表格單元
        commitEdit(textfield.getText());
      } 
      else if (e.getCode() == KeyCode.ESCAPE) {
        // 取消編輯樹狀表格單元
        cancelEdit();
      }
    });
  }

  setText(null);
  // 將樹狀表格單元設定為文字欄位
  setGraphic(textfield);
  textfield.selectAll();
}
...
須注意的是,由於需要編輯樹狀表格單元,因此須以TreeTableView類別的setEditable()方法設定Tree Table View為可編輯狀態,並以TreeTableView類別的setCellFactory()方法以下列方式設定Cell Factory的回呼函式:
TreeTableView<Department> treeTableView = null;
treeTableView = new TreeTableView<>();
// 設定Tree Table View為可編輯狀態
treeTableView.setEditable(true);

// 建立直行
TreeTableColumn<Department, String> treeTableColumn1 = 
  new TreeTableColumn<>("Faculty");

// 設定Cell Factory的回呼函式
treeTableColumn1.setCellFactory(
  (TreeTableColumn<Department, String> value) -> 
  new TextFieldCellCallback());
...
【執行結果】
藉由繼承TreeTableCell類別之自定類別做為setCellFactory()方法的回呼函式,可使用各種物件取代原樹狀表格單元,以便編輯樹狀表格單元內容

此方式雖然可行,但必須覆寫TreeTableCell類別的startEdit()cancelEdit()commitEdit()updateItem()方法,因此有一定難度。 此外,JavaFX提供以下類別,可分別以核取方塊、選項方塊、複合方塊、進度列與文字欄位等取代原樹狀表格單元類別彼此之間所提供的方法十分類似,使用上亦很容易:
  • 核取方塊樹狀表格單元:CheckBoxTreeTableCell.
  • 選項方塊樹狀表格單元:ChoiceBoxTreeTableCell.
  • 複合方塊樹狀表格單元:ComboBoxTreeTableCell.
  • 進度列樹狀表格單元:ProgressBarTreeTableCell.
  • 文字欄位樹狀表格單元:TextFieldTreeTableCell.
CheckBoxTreeTableCell類別類似於CheckBoxTableCell類別,以建立核取方塊樣式的樹狀表格單元,功能與核取方塊十分類似,其選取狀態分為未勾選與已勾選。以下範例以TreeTableColumn類別的setCellFactory()方法設定Cell Factory為CheckBoxTreeTableCell,並以其forTreeTableColumn()方法以核取方塊取代原樹狀表格單元:
// 建立直行
TreeTableColumn<Department, Boolean> treeTableColumn5 = 
  new TreeTableColumn<>("Check");
// 設定直行最小寬度
treeTableColumn5.setPrefWidth(60);
// 設定直行標題的圖像
treeTableColumn5.setGraphic(new ImageView(new Image(
  getClass().getResourceAsStream("images/checkbox.gif"))));
// 設定Cell Factory
treeTableColumn5.setCellFactory(
  CheckBoxTreeTableCell.forTreeTableColumn(treeTableColumn5));
treeTableColumn5.setEditable(true);
...
【執行結果】
ChoiceBoxTreeTableCell類別類似於ChoiceBoxTableCell類別,以建立選項方塊樣式的樹狀表格單元,功能與選項方塊十分類似,當點選樹狀表格單元時,則以「下拉式選單」的方式列出選項項目,由於包含一個以上的選項項目,因此ChoiceBoxTreeTableCell類別必須以陣列定義一組選項項目,以做為「下拉式選單」的選項內容。以下範例以TreeTableColumn類別的setCellFactory()方法設定Cell Factory為ChoiceBoxTreeTableCell,並以其forTreeTableColumn()方法以選項方塊取代原樹狀表格單元,當點選樹狀表格單元時,則以「下拉式選單」的方式列出選項項目,其中以ObservableList設定選項方塊的選項項目:
// 建立直行
TreeTableColumn<Department, String> treeTableColumn5 = 
  new TreeTableColumn<>("Phone");
// 設定直行最小寬度
treeTableColumn5.setPrefWidth(60);
// 設定直行標題的圖像
treeTableColumn5.setGraphic(new ImageView(new Image(
  getClass().getResourceAsStream("images/choicebox.gif"))));

// 設定選項方塊的選項項目
ObservableList phonelists = FXCollections.observableArrayList(
  "4059", "4330", "5390",
  "5600", "5720", "6399",
  "6400", "6401", "6506");

// 設定Cell Factory
treeTableColumn5.setCellFactory(
  ChoiceBoxTreeTableCell.forTreeTableColumn(phonelists));
treeTableColumn5.setEditable(true);
...
【執行結果】
ComboBoxTreeTableCell類別類似於ComboBoxTableCell類別,以建立複合方塊樣式的樹狀表格單元,功能與複合方塊十分類似。以下範例以TreeTableColumn類別的setCellFactory()方法設定Cell Factory為ComboBoxTreeTableCell,並以其forTreeTableColumn()方法以複合方塊取代原樹狀表格單元,當點選樹狀表格單元時,則以「下拉式選單」的方式列出選項項目,其中以ObservableList設定複合方塊的選項項目:
// 建立直行
TreeTableColumn<Department, String> treeTableColumn5 = 
  new TreeTableColumn<>("Phone");
// 設定直行最小寬度
treeTableColumn5.setPrefWidth(60);
// 設定直行標題的圖像
treeTableColumn5.setGraphic(new ImageView(new Image(
  getClass().getResourceAsStream("images/combobox.gif"))));

// 設定選項方塊的選項項目
ObservableList phonelists = FXCollections.observableArrayList(
  "4059", "4330", "5390",
  "5600", "5720", "6399",
  "6400", "6401", "6506");

// 設定Cell Factory
treeTableColumn5.setCellFactory(
  ComboBoxTreeTableCell.forTreeTableColumn(phonelists));
treeTableColumn5.setEditable(true);
...
【執行結果】
TextFieldTreeTableCell類別類似於TextFieldTableCell類別,以建立文字欄位樣式的樹狀表格單元,當以滑鼠點選樹狀表格單元時,則將原樹狀表格單元轉換為文字欄位,以此編輯樹狀表格單元內容。以下範例以TreeTableColumn類別的setCellFactory()方法設定Cell Factory為TextFieldTreeTableCell,並以其forTreeTableColumn()方法以文字欄位取代原樹狀表格單元,當以滑鼠點選樹狀表格單元時,則將原樹狀表格單元轉換為文字欄位,以此編輯樹狀表格單元內容:
// 建立直行
TreeTableColumn<Department, String> treeTableColumn1 = 
  new TreeTableColumn<>("Faculty");
// 設定直行最小寬度
treeTableColumn1.setPrefWidth(80);
// 設定直行標題的圖像
treeTableColumn1.setGraphic(new ImageView(new Image(
  getClass().getResourceAsStream("images/faculty.gif"))));

// 設定直行對應於資料陣列的順序
treeTableColumn1.setCellValueFactory(
  (TreeTableColumn.CellDataFeatures<Department, String> param) ->
  new ReadOnlyStringWrapper(param.getValue().getValue().getFaculty())
);

// 設定Cell Factory
treeTableColumn1.setCellFactory(
  TextFieldTreeTableCell.forTreeTableColumn());
treeTableColumn1.setEditable(true);
...
【執行結果】
【參考資料】

[1] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[2] JavaFX:http://www.oracle.com/technetwork/java/javafx
[3] JavaFX 8.0 API Specification.
[4] Java Platform, Standard Edition 8 API Specification.

© Chia-Hui Huang

2014年10月17日 星期五

JavaFX Cell Factory (3)

表格 (Table) 為視窗程式中重要的物件之一,常運用於試算表與資料庫,表格以直行 (Column) 與橫列 (Row) 的方式呈現資料,每一直行與橫列交會之處稱為表格單元 (Table Cell)。

表格物件由Table View、Table Column、Table Row與Table Cell所組成,TableView類別用以置放直行、橫列與表格單元,並顯示表格物件,為表格物件的最上層,如同TreeViewComboBoxListView類別一般,可修改各表格單元的形式,不同的是,JavaFX的表格分別依據直行或橫列設定其Cell Factory。

表格單元的類別為javafx.scene.control.TableCell,繼承自TableCell的類別包括:

  • 核取方塊表格單元:CheckBoxTableCell.
  • 選項方塊表格單元:ChoiceBoxTableCell.
  • 複合方塊表格單元:ComboBoxTableCell.
  • 進度列表格單元:ProgressBarTableCell.
  • 文字欄位表格單元:TextFieldTableCell.
因此可使用核取方塊、選項方塊、複合方塊、進度列與文字欄位等取代原表格單元,較樹狀單元多了進度列物件。

以下範例以繼承TableCell類別之自定TextFieldCellCallback類別做為setCellFactory()方法的回呼函式。在TextFieldCellCallback類別的建構函式中,以setAlignment()方法設定欄位內容為置中對齊,並建立快顯選單,當在表格上按下滑鼠右鍵時,將顯示快顯選單以新增一橫列:

private final class TextFieldCellCallback extends TableCell<> {
  TextField textfield;
  ContextMenu contextmenu = new ContextMenu();

  // 建構函式
  public TextFieldCellCallback() {
    // 設定欄位內容為置中對齊
    setAlignment(Pos.CENTER);
    // 建立快顯選單
    MenuItem menuitem = new MenuItem("Add New Row");
    menuitem.setGraphic(new ImageView(new Image(
      getClass().getResourceAsStream("images/row.gif"))));

    menuitem.setOnAction((ActionEvent e) -> {
      // 新增一表格橫列
      data.add(new Product("", "", "", "", ""));
    });
    contextmenu.getItems().add(menuitem);
  }
  ...
}
...
TextFieldCellCallback類別中,分別覆寫TableCell類別的startEdit()cancelEdit()updateItem()方法以處理開始編輯、取消編輯與更新表格單元等。

以開始編輯為例,在覆寫startEdit()方法中,當以滑鼠點選表格單元時,則以setGraphic()方法將表格單元設定為文字欄位,以此編輯表格單元的內容。此外,並以setOnKeyReleased()方法處理當在表格單元文字欄位釋放鍵盤按鍵時之事件,若按下Enter鍵,代表完成輸入,則以commitEdit()方法完成編輯表格單元;若按下ESC鍵,代表取消輸入,則以cancelEdit()方法取消編輯表格單元:

// 覆寫TableCell類別的startEdit()方法
@Override public void startEdit() {
  super.startEdit();

  if (textfield == null) {
    textfield = new TextField(getItem() == null ? "" : getItem());
    textfield.setMinWidth(this.getWidth()-this.getGraphicTextGap()*2);
    
    textfield.setOnKeyReleased((KeyEvent e) -> {
      if (e.getCode() == KeyCode.ENTER) {
        // 完成編輯表格單元
        commitEdit(textfield.getText());
      } 
      else if (e.getCode() == KeyCode.ESCAPE) {
        // 取消編輯表格單元
        cancelEdit();
      }
    });
  }

  setText(null);
  // 將表格單元設定為文字欄位
  setGraphic(textfield);
  textfield.selectAll();
}
...
須注意的是,由於需要編輯表格單元,因此須以TableView類別的setEditable()方法設定Table View為可編輯狀態,並以TableView類別的setCellFactory()方法以下列方式設定Cell Factory的回呼函式:
TableView<Product> tableView = new TableView<>();
// 設定Table View為可編輯狀態
tableView.setEditable(true);

TableColumn column[] = new TableColumn[title.length];

column[i].setCellFactory(new Callback<TableColumn<Product, String>, 
  TableCell<Product, String>>() {
  @Override public TableCell<Product, String> call(
    TableColumn<Product, String> value) {
      return new TextFieldCellCallback();
  }
});
...
【執行結果】 
藉由繼承TableCell類別之自定類別做為setCellFactory()方法的回呼函式,可使用各種物件取代原表格單元,以便編輯表格單元內容

此方式雖然可行,但必須覆寫TableCell類別的startEdit()cancelEdit()commitEdit()updateItem()方法,因此有一定難度。 此外,JavaFX提供以下類別,可分別以核取方塊、選項方塊、複合方塊、進度列與文字欄位等取代原表格單元類別彼此之間所提供的方法十分類似,使用上亦很容易:
  • 核取方塊表格單元:CheckBoxTableCell.
  • 選項方塊表格單元:ChoiceBoxTableCell.
  • 複合方塊表格單元:ComboBoxTableCell.
  • 進度列表格單元:ProgressBarTableCell.
  • 文字欄位表格單元:TextFieldTableCell.
CheckBoxTableCell類別類似於CheckBox類別,以建立核取方塊樣式的表格單元,功能與核取方塊十分類似,其選取狀態分為未勾選與已勾選。以下範例以TableColumn類別的setCellFactory()方法設定Cell Factory為CheckBoxTableCell,並以其forTableColumn()方法以核取方塊取代原表格單元,相較於之前的範例,範例更為精簡:
TableColumn column[] = new TableColumn[title.length];

for (int i=0; i<title.length; i++) {
  column[i] = new TableColumn(title[i]);
  column[i].setMinWidth(width[i]);
  // 設定直行標題的圖像
  column[i].setGraphic(new ImageView(new Image(
    getClass().getResourceAsStream("images/icon" + (i+1) + ".gif"))));

  if (i!=4) {
    // 設定直行對應於資料陣列的順序
    column[i].setCellValueFactory(
      new PropertyValueFactory<>(cellValue[i]));
  }
  else { // CheckBoxTableCell
    // 設定直行對應於資料陣列的順序
    column[i].setCellValueFactory(
      new PropertyValueFactory<>(cellValue[i]));

    // 設定Cell Factory
    column[i].setCellFactory(
      CheckBoxTableCell.forTableColumn(column[i]));
    column[i].setEditable(true);
  }
}
...
【執行結果】 
ChoiceBoxTableCell類別類似於ChoiceBox類別,以建立選項方塊樣式的表格單元,功能與選項方塊十分類似,當點選表格單元時,則以「下拉式選單」的方式列出選項項目,由於包含一個以上的選項項目,因此ChoiceBoxTableCell類別必須以陣列定義一組選項項目,以做為「下拉式選單」的選項內容。以下範例以TableColumn類別的setCellFactory()方法設定Cell Factory為ChoiceBoxTableCell,並以其forTableColumn()方法以選項方塊取代原表格單元,當點選表格單元時,則以「下拉式選單」的方式列出選項項目,其中以ObservableList設定選項方塊的選項項目:
TableColumn column[] = new TableColumn[title.length];
...

// 設定選項方塊的選項項目
ObservableList<String> items = FXCollections.observableArrayList(
  "JX001", "JX002", "JX003", "JX004", "JX005", 
  "JX006", "JX007", "JX008", "JX009", "JX010");

column[i] = new TableColumn(title[i]);
// 設定直行對應於資料陣列的順序
column[i].setCellValueFactory(
  new PropertyValueFactory<>(cellValue[i]));
// 設定Cell Factory
column[i].setCellFactory(
  ChoiceBoxTableCell.forTableColumn(items));
column[i].setEditable(true);
...
【執行結果】 
ComboBoxTableCell類別類似於ComboBox類別,以建立複合方塊樣式的表格單元,功能與複合方塊十分類似。以下範例以TableColumn類別的setCellFactory()方法設定Cell Factory為ComboBoxTableCell,並以其forTableColumn()方法以複合方塊取代原表格單元,當點選表格單元時,則以「下拉式選單」的方式列出選項項目,其中以ObservableList設定複合方塊的選項項目:
TableColumn column[] = new TableColumn[title.length];
...

// 設定選項方塊的選項項目
ObservableList<String> items = FXCollections.observableArrayList(
  "JX001", "JX002", "JX003", "JX004", "JX005", 
  "JX006", "JX007", "JX008", "JX009", "JX010");

column[i] = new TableColumn(title[i]);
// 設定直行對應於資料陣列的順序
column[i].setCellValueFactory(
  new PropertyValueFactory<>(cellValue[i]));
// 設定Cell Factory
column[i].setCellFactory(
  ComboBoxTableCell.forTableColumn(items));
column[i].setEditable(true);
...
【執行結果】 
ProgressBarTableCell類別類似於ProgressBar類別,以建立進度列樣式的表格單元,顯示進度之用。為處理進度列顯示進度,範例以繼承Task抽象類別之自定ProgressBarTask類別做為處理進度之用:
private final class ProgressBarTask extends Task<Void> {
  int waiting; 
  int pausing;

  ProgressBarTask(int _waiting, int _pausing) {
    this.waiting = _waiting;
    this.pausing = _pausing;
  }

  @Override protected Void call() throws Exception {
    this.updateProgress(ProgressIndicator.INDETERMINATE_PROGRESS, 1);
    this.updateMessage("Waiting...");
    Thread.sleep(waiting);
    this.updateMessage("Running...");
    
    for (int i=0; i<500; i++) {
      updateProgress((1.0*i)/500, 1);
      Thread.sleep(pausing);
    }
    this.updateMessage("Done");
    this.updateProgress(1, 1);
    return null;
  }
}
...
接著以TableColumn類別的setCellFactory()方法設定Cell Factory為ProgressBarTableCell,並以其forTableColumn()方法以進度列取代原表格單元:
Random randomv = new Random();

for (int i=0; i<10; i++) {
  tableView.getItems().add(new ProgressBarTask(
    randomv.nextInt(5000)+1000, randomv.nextInt(50)+10));
}

TableColumn<ProgressBarTask, Double> column1 = 
  new TableColumn("Progress");
column1.setGraphic(new ImageView(new Image(
  getClass().getResourceAsStream("images/icon1.gif"))));
// 設定直行對應於資料陣列的順序
column1.setCellValueFactory(new PropertyValueFactory<>("progress"));
// 設定Cell Factory
column1.setCellFactory(
  ProgressBarTableCell.<ProgressBarTask>forTableColumn());

TableColumn<ProgressBarTask, String> column2 = 
  new TableColumn("Status");
column2.setGraphic(new ImageView(new Image(
  getClass().getResourceAsStream("images/icon2.gif"))));
// 設定直行對應於資料陣列的順序
column2.setCellValueFactory(new PropertyValueFactory<>("message"));
column2.setPrefWidth(80);
...
【執行結果】 
TextFieldTableCell類別類似於TextField類別,以建立文字欄位樣式的表格單元,當以滑鼠點選表格單元時,則將原表格單元轉換為文字欄位,以此編輯表格單元內容。以下範例以TableColumn類別的setCellFactory()方法設定Cell Factory為TextFieldTableCell,並以其forTableColumn()方法以文字欄位取代原表格單元,當以滑鼠點選表格單元時,則將原表格單元轉換為文字欄位,以此編輯表格單元內容:
TableColumn column[] = new TableColumn[title.length];
...

column[i] = new TableColumn(title[i]);
column[i].setMinWidth(width[i]);
// 設定直行對應於資料陣列的順序
column[i].setCellValueFactory(
  new PropertyValueFactory<>(cellValue[i]));
// 設定Cell Factory
column[i].setCellFactory(TextFieldTableCell.forTableColumn());
column[i].setEditable(true);
...
【執行結果】 
【參考資料】

[1] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[2] JavaFX:http://www.oracle.com/technetwork/java/javafx
[3] JavaFX 8.0 API Specification.
[4] Java Platform, Standard Edition 8 API Specification.

© Chia-Hui Huang