2014年3月29日 星期六

深入研究JavaFX 2

This is my 36th book, "Professional JavaFX 2".

終於,2014年又多了第二本新書,我的第36本書 –「深入研究JavaFX 2」。

Welcome to JavaFX.

2008年12月Sun Microsystems發表JavaFX 1.0,以JavaFX Script語法的方式開發R.I.A.(Rich Internet Application)應用程式,自此筆者就一直研究JavaFX,但很可惜的是由於JavaFX Script與傳統Java差異甚多,因此JavaFX並未受到很大的重視。2009年Sun Microsystems被Oracle併購,有好一陣子沒有Java與JavaFX的消息,筆者也感覺有些落寞。

直到2011年10月Oracle發表代號Presidio的JavaFX 2.0,以Java語法取代原JavaFX Script,新增多個API套件與FXML,讓JavaFX煥然一新。2012年8月,Oracle正式將JavaFX 2.2併入Java S.E. 7 Update 6之中,透過相同的Java編譯器與直譯器則可編譯與執行JavaFX程式,讓JavaFX變得更為方便。

JavaFX以「Write Once, Deploy Anywhere」的概念開發R.I.A.應用程式,可同時以Java Applet、Java Web Start與視窗應用程式的形式執行,除了提供類似於Java Swing的GUI物件之外,JavaFX為強調動畫與多媒體效果,增加了圖表、多媒體、CSS樣式、2D、3D、視覺效果等功能,並支援處理觸控螢幕與觸控板的手勢與觸控事件,最重要的是JavaFX改進原有Java Swing繁複的程式架構,讓JavaFX程式更為精簡,相較於Java Swing,確實更加精簡易學,大有取代Java AWT與Java Swing之勢。

JavaFX的重新出發,讓筆者感到相當振奮,為完成此書,將JavaFX徹底研究一遍,每每學到一個新功能,都讓筆者十分興奮,急於與同好分享。

完稿之時,下一版本JavaFX 8.0 Early Access已開放下載測試,程式更為簡單,並新增支援3D、Shaders for OpenGL and Direct3D、Java S.E. Embedded、感測、列印、Rich Text格式、JavaScript、HTML 5等功能,讓人期待不已。 筆者自1995年開始接觸Java,至今已18年了,對Java熱情依舊,看著JavaFX的發展,如同人生的際遇一般起起伏伏,衷心期待JavaFX能在R.I.A.的領域中擁有自己的一片天空。 

本書章節包括:

1. JavaFX
2. JavaFX程式
3. Layout Pane
4. 標籤與文字
5. 按鈕
6. 選項項目
7. 捲軸、滑動軸、進度指示器與進度列
8. 選單
9. 文字編輯物件
10. 窗格
11. 對話盒
12. Web
13. 樹
14. 表格
15. 圖表
16. JavaFX事件
17. 繪圖
18. 多媒體
© Chia-Hui Huang

JavaFX Effect (2) Gradient

JavaFX的漸層 (Gradient) 效果分為線性漸層 (Linear Gradient) 與放射性漸層 (Radial Gradient) 兩種,分別由LinearGradientRadialGradient類別處理,皆繼承自Paint抽象類別。Gradient原意為傾斜度,但其效果有如漸層一般,因此譯為漸層。漸層需由兩種以上的顏色組成,各漸層顏色的比例由Stop類別處理。

線性漸層將數種顏色以線性的方式依比例漸進填滿物件,其效果如下圖所示:
線性漸層由javafx.scene.effect.LinearGradient類別處理,其建構函式如下:

  public LinearGradient(double startX, double startY,
    double endX, double endY, boolean proportional,
    CycleMethod cycleMethod, List<Stop> stops)
  public LinearGradient(double startX, double startY,
    double endX, double endY, boolean proportional,
    CycleMethod cycleMethod, Stop... stops)

其中參數:

1. startX:設定漸層軸始點的水平座標,預設為0.0。
2. startY:設定漸層軸始點的垂直座標,預設為0.0。
3. endX:設定漸層軸終點的水平座標,預設為1.0。
4. endY:設定漸層軸終點的垂直座標,預設為1.0。
5. proportional:設定漸層是否以成正比的方式填滿物件。
6. stops:漸層需由兩種以上的顏色組成,此參數設定各漸層顏色的比例。
7. cycleMethod:設定漸層循環方法,為以下的常數值:
  • CycleMethod.NO_CYCLE
  • CycleMethod.REFLECT
  • CycleMethod.REPEAT
請參考以下範例示範以LinearGradient類別處理線性漸層,其中漸層需由兩種以上的顏色組成,各漸層顏色的比例由Stop類別處理,範例分別以紅色、橘色與黃色三種顏色組成漸層:


// 設定各漸層顏色的比例
Stop[] stops = new Stop[] { 
  new Stop(0.0, Color.RED),
  new Stop(0.5, Color.ORANGE),
  new Stop(1.0, Color.YELLOW)
};


接著以LinearGradient類別的建構函式分別設定上述線性漸層的相關參數:


// 設定線性漸層
LinearGradient lineargradient1 = new LinearGradient(
  0.0, 0.0, 1.0, 0.0, true, CycleMethod.REFLECT, stops);


當完成上述設定之後,則以setFill()方法設定物件的漸層效果:


Text text1 = new Text();
text1.setText("Linear Gradient");
// 設定漸層效果
text1.setFill(lineargradient1);
text1.setFont(Font.font("Arial Black", FontWeight.BOLD, 60));


需注意的是,LinearGradient類別只有getXXX()取得相關參數的方法,並沒有setXXX()設定參數的方法,因此只能以LinearGradient類別的建構函式設定。

【執行結果】

請比較漸層軸始點與終點的水平與垂直座標之差異。

接著說明放射性漸層。

放射性漸層將數種顏色以放射狀的方式依比例漸進填滿物件,各漸層顏色的比例由Stop類別處理,其效果如下圖所示:
放射性漸層由javafx.scene.effect.RadialGradient類別處理,其建構函式如下:

  public RadialGradient(double focusAngle, double focusDistance, 
    double centerX, double centerY, double radius, 
    boolean proportional, CycleMethod cycleMethod, List<Stop> stops)
  public RadialGradient(double focusAngle, double focusDistance, 
    double centerX, double centerY, double radius, 
    boolean proportional, CycleMethod cycleMethod, Stop... stops)

其中參數:

1. focusAngle:設定由漸層中心點至第一個漸層顏色焦點的角度。
2. focusDistance:設定由漸層中心點至第一個漸層顏色焦點的距離,介於0.0與1.0之間。
3. centerX:設定漸層中心點的水平座標,預設為0.0。
4. centerY:設定漸層中心點的垂直座標,預設為0.0。
5. radius:設定漸層的半徑,預設為1.0。
6. proportional:設定漸層是否以成正比的方式填滿物件。
7. stops:設定各漸層顏色的比例。
8. cycleMethod:設定漸層循環方法。

請參考以下範例示範以RadialGradient類別處理放射性漸層,其中漸層需由兩種以上的顏色組成,各漸層顏色的比例由Stop類別處理,範例分別以紅色、橘色與黃色三種顏色組成漸層:


// 設定各漸層顏色的比例
Stop[] stops = new Stop[] { 
  new Stop(0.0, Color.RED),
  new Stop(0.5, Color.ORANGE),
  new Stop(1.0, Color.YELLOW)
};


接著以RadialGradient類別的建構函式分別設定上述放射性漸層的相關參數:


// 設定放射性漸層
RadialGradient radialgradient1 = new RadialGradient(
  0.0, 0.0, 0.5, 0.5, 0.1, true, CycleMethod.REFLECT, stops);


當完成上述設定之後,則以setFill()方法設定物件的漸層效果:


Text text1 = new Text();
text1.setText("Radial Gradient");
// 設定漸層效果
text1.setFill(radialgradient1);
text1.setFont(Font.font("Arial Black", FontWeight.BOLD, 60));


需注意的是,RadialGradient類別與LinearGradient類別一樣,只有getXXX()取得相關參數的方法,並沒有setXXX()設定參數的方法,因此只能以RadialGradient類別的建構函式設定。

【執行結果】

【參考資料】

[1] 黃嘉輝,深入研究JavaFX 2。
[2] 黃嘉輝,JavaFX遊戲程式設計。
[3] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[4] JavaFX:http://www.oracle.com/technetwork/java/javafx
[5] JavaFX 8.0 API Specification.
[6] Java Platform, Standard Edition 8 API Specification.

© Chia-Hui Huang

2014年3月28日 星期五

JavaFX Effect (1) Shadow

JavaFX之所以特殊,除了提供豐富的GUI物件、Builder、CSS樣式等之外,最重要的是JavaFX提供物件的特殊效果 (Effect),更突顯物件的立體感與層次感,特殊效果分為陰影、漸層、模糊、反射、高光與深褐色調等。

JavaFX的陰影 (Shadow) 效果分為Drop Shadow與Inner Shadow兩種,分別由DropShadowInnerShadow類別處理,皆繼承自Effect抽象類別,物件加入陰影效果,更能突顯物件的立體感。

Drop Shadow其陰影在物件的外圍,如同光由物件的上方照射所產生的陰影,因此稱為Drop,其效果如下圖所示:
Drop Shadow由javafx.scene.effect.DropShadow類別處理,其建構函式如下:

  public DropShadow()
  public DropShadow(double radius, Color color)
  public DropShadow(double radius, double offsetX, double offsetY,
    Color color)
  public DropShadow(BlurType blurType, Color color, double radius,
    double spread, double offsetX, double offsetY)

其中參數:

1. radius:設定陰影模糊核心的半徑。
2. color:設定陰影的顏色。
3. offsetX:設定陰影的水平偏移 (Offset) 位置。
4. offsetY:設定陰影的垂直偏移 (Offset) 位置。
5. spread:設定陰影模糊核心半徑的擴散比例,介於0.0與1.0之間。
6. blurType:設定模糊陰影的類型,為以下的常數值:
  • BlurType.GAUSSIAN
  • BlurType.ONE_PASS_BOX
  • BlurType.TWO_PASS_BOX
  • BlurType.THREE_PASS_BOX
請參考以下範例示範以DropShadow類別處理陰影,並介紹以下之方法:
  • setColor():設定陰影的顏色。
  • setOffsetX():設定陰影的水平偏移位置。
  • setOffsetY():設定陰影的垂直偏移位置。
  • setRadius():設定陰影模糊核心的半徑。
當完成上述設定之後,則以setEffect()方法設定物件的陰影效果:


DropShadow dropshadow = new DropShadow();
// 設定陰影的顏色
dropshadow.setColor(Color.GREY);
// 設定陰影的水平偏移位置
dropshadow.setOffsetX(5.0);
// 設定陰影的垂直偏移位置
dropshadow.setOffsetY(5.0);
// 設定陰影模糊核心的半徑
dropshadow.setRadius(5.0);

Text text = new Text();

// 設定陰影效果
text.setEffect(dropshadow);
text.setText("Drop Shadow");
text.setFill(Color.BLACK);
text.setFont(Font.font("Arial Black", FontWeight.BOLD, 60));


【執行結果】
接著說明Inner Shadow。

Inner Shadow其陰影在物件的內部,因此稱為Inner,其效果如下圖所示:
Inner Shadow與Drop Shadow的最大差別在於,除了Inner Shadow的陰影在物件內部之外,Drop Shadow以spread參數設定陰影模糊核心半徑的擴散 (Spread) 比例,而Inner Shadow則是以choke參數設定陰影模糊核心半徑的扼制 (Choke) 比例,扼制比例介於0.0與1.0之間,若為0.0,則陰影將全由模糊邏輯決定;若為1.0,則陰影將由物件內部擴散至物件邊緣,導致陰影幾乎佔據整個物件內部。

Inner Shadow由javafx.scene.effect.InnerShadow類別處理,其建構函式如下:

  public InnerShadow()
  public InnerShadow(double radius, Color color)
  public InnerShadow(double radius, double offsetX, double offsetY,
    Color color)
  public InnerShadow(BlurType blurType, Color color, double radius,
    double choke, double offsetX, double offsetY)

其中參數:

1. radius:設定陰影模糊核心的半徑。
2. color:設定陰影的顏色。
3. offsetX:設定陰影的水平偏移位置。
4. offsetY:設定陰影的垂直偏移位置。
5. choke:設定陰影模糊核心半徑的扼制比例,介於0.0與1.0之間。
6. blurType:設定模糊陰影的類型,為以下的常數值:
  • BlurType.GAUSSIAN
  • BlurType.ONE_PASS_BOX
  • BlurType.TWO_PASS_BOX
  • BlurType.THREE_PASS_BOX
請參考以下範例示範以InnerShadow類別處理陰影,並介紹以下之方法:
  • setColor():設定陰影的顏色。
  • setChoke():設定陰影模糊核心半徑的扼制比例。
  • setOffsetX():設定陰影的水平偏移位置。
  • setOffsetY():設定陰影的垂直偏移位置。
  • setRadius():設定陰影模糊核心的半徑。
當完成上述設定之後,則以setEffect()方法設定物件的陰影效果:



InnerShadow innershadow = new InnerShadow();
// 設定陰影的顏色
innershadow.setColor(Color.GREY);
// 設定陰影模糊核心半徑的扼制比例
innershadow.setChoke(0.5);
// 設定陰影的水平偏移位置
innershadow.setOffsetX(5.0);
// 設定陰影的垂直偏移位置
innershadow.setOffsetY(5.0);
// 設定陰影模糊核心的半徑
innershadow.setRadius(5.0);

Text text = new Text();
// 設定陰影效果
text.setEffect(innershadow);
text.setText("Inner Shadow");
text.setFill(Color.web("#BBBBBB"));
text.setFont(Font.font("Arial Black", FontWeight.BOLD, 60));


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

[1] 黃嘉輝,深入研究JavaFX 2。
[2] 黃嘉輝,JavaFX遊戲程式設計。
[3] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[4] JavaFX:http://www.oracle.com/technetwork/java/javafx
[5] JavaFX 8.0 API Specification.
[6] Java Platform, Standard Edition 8 API Specification.

© Chia-Hui Huang


2014年3月25日 星期二

JavaFX 8 Event with Lambda Expression

Lambda Expression是Java SE 8.0新增的一項語言描述方式,其語法如下


(argument) -> (body)


Lambda Expression是一種匿名函數 (Anonymous Function),沒有Method Declaration (方法宣告),亦不需要Modifier (修飾詞) 與Return Value Declaration (回傳值宣告) 等,因此程式更為精簡。

以下是一些簡單的例子


(int a, int b) -> {return a + b;}

() -> System.out.println("...");

(String value) -> {return value;}


我們可以將Lambda Expression運用於JavaFX 8的事件處理上,讓處理事件的方式更為簡單

首先回顧JavaFX 2處理事件的程式架構

JavaFX 2簡化處理事件的方式,不像Java以不同的Listener介面處理各類事件,JavaFX 2僅以EventHandler介面處理各類事件,且介面僅提供handle()方法,並以事件類型 (Event Type) 定義各類事件,其基礎類別為javafx.event.Event,繼承自Event類別的事件類別分別如下所示,其中較特殊的是手勢與觸控事件:
  • ActionEvent:動作事件。
  • ContextMenuEvent:快顯選單事件。
  • DragEvent:拖曳事件,適用於滑鼠與觸控裝置。
  • GestureEvent:手勢事件,適用於觸控裝置。
  • InputEvent:輸入事件。
  • InputMethodEvent輸入方法事件。
  • KeyEvent按鍵事件。
  • MediaErrorEvent多媒體錯誤事件。
  • MouseEvent滑鼠事件。
  • MouseDragEvent滑鼠拖曳事件,有別於DragEvent。
  • RotateEvent旋轉事件,適用於觸控裝置。
  • ScrollEvent捲動事件,適用於滑鼠與觸控裝置。
  • SwipeEvent滑動事件,適用於觸控裝置。
  • TouchEvent觸控事件,適用於觸控裝置。
  • WebEventWeb Engine事件。
  • WindowEvent視窗事件。
  • WorkerStateEventWorker狀態改變時之事件。
  • ZoomEvent縮放事件,適用於觸控裝置。
JavaFX 2處理事件的第一種方式是以物件的setOnXXX()方法設定處理事件的Event Handler函式,其語法如下,其中[Event_TYPE]為上述之事件類別:


[object].setOnXXX(new EventHandler<[Event_TYPE]>() {
  @Override public void handle([Event_TYPE] e) {
    ...
  }
});


以按鈕的動作事件為例,其設定Event Handler函式的方法為setOnAction()、事件類別為ActionEvent


Button button = new Button();

button.setOnAction(new EventHandler<ActionEvent>() {

  @Override public void handle(ActionEvent e) {
    ...
  }
});


亦可使用建立類別的方式處理:


Button button = new Button();
button.setOnAction(onActionEventHandler);
...

EventHandler onActionEventHandler = new EventHandler<ActionEvent>() {

  @Override public void handle(ActionEvent e) {
    ...
  }
};


透過Lambda Expression可將上述程式精簡如下,省略EventHandler介面的描述


Button button = new Button();

button.setOnAction(
(ActionEvent e) -> {
  ...
});


或以建立類別的方式處理:


Button button = new Button();
button.setOnAction(onActionEventHandler);
...

EventHandler onActionEventHandler = 

  (EventHandler<ActionEvent>)(ActionEvent e) -> {
  ...
};


更進一步可依賴編譯器的型態推斷 (Type Inference),讓程式更精簡

Button button = new Button();

button.setOnAction(e -> {
  ...
});


以上由良葛格指導,特別在此感謝良葛格。

JavaFX 2處理事件的第二種方式是以物件的addEventHandler()方法註冊事件的Event Handler,其語法如下,其中[eventType]代表事件類型、[Event_TYPE]為前述之事件類別:


[object].addEventHandler([eventType], new EventHandler<[Event_TYPE]>() {
  @Override public void handle([Event_TYPE] e) {
    ...
  }
});


以按鈕的動作事件為例,其事件類型為ActionEvent.ACTION、事件類別為ActionEvent,註冊Event Handler的程式架構如下:


Button button = new Button();

button.addEventHandler(ActionEvent.ACTION,

  new EventHandler<ActionEvent>() {
    @Override public void handle(ActionEvent e) {
      ...
    }
  }
);


透過Lambda Expression可將上述程式精簡如下


Button button = new Button();

button.addEventHandler(ActionEvent.ACTION, (ActionEvent e) -> {
  ...
};


JavaFX 2處理事件的第三種方式是以物件的addEventFilter()方法註冊事件的Event Filter,其語法如下,與Event Handler幾乎一樣,其中[eventType]代表事件類型、[Event_TYPE]為前述之事件類別:


[object].addEventFilter([eventType], new EventHandler<[Event_TYPE]>() {
  @Override public void handle([Event_TYPE] e) {
    ...
  }
});


以按鈕的動作事件為例,其事件類型為ActionEvent.ACTION、事件類別為ActionEvent,註冊Event Filter的程式架構如下:


Button button = new Button();

button.addEventFilter(ActionEvent.ACTION,

  new EventHandler<ActionEvent>() {
    @Override public void handle(ActionEvent e) {
      ...
    }
  }
);


透過Lambda Expression可將上述程式精簡如下


Button button = new Button();

button.addEventFilter(ActionEvent.ACTION, (ActionEvent e) -> {
  ...
};


【參考資料】

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

© Chia-Hui Huang

2014年3月23日 星期日

JavaFX 8移除各物件之Builder類別

2014年3月18日正式釋出Java SE Development Kit 8,發現與之前的Beta版有很大的差異,特別是將各物件對應之Builder類別移除。

蠻好奇Oracle為什麼要移除Builder這個功能?

回顧一下JavaFX 2的Builder類別

Builder是JavaFX 2一個特殊功能與語法,以Button類別 (按鈕) 為例,其對應之Builder類別為ButtonBuilder,透過ButtonBuilder類別可精簡程式長度,但程式的撰寫方式與以往的Java程式不同。

ButtonBuilder類別的方法包括: 
  • create():建立ButtonBuilder的實體。 
  • build():以ButtonBuilder物件建立按鈕的實體,並回傳Button物件。
  • cancelButton():設定按鈕是否為取消按鈕,則設定按鈕的cancelButton屬性值,若為true,則為取消按鈕。 
  • defaultButton():設定按鈕是否為預設按鈕,則設定按鈕的defaultButton屬性值,若為true,則為預設按鈕。 
ButtonBuilder類別建立按鈕的程式架構如下,首先以create()方法建立ButtonBuilder的實體,最後再以build()方法以ButtonBuilder物件建立Button物件,其間並以上述方法設定按鈕的相關屬性,各方法可為任意順序或省略: 


Button button = ButtonBuilder.create()
  .cancelButton(...)
  .defaultButton(...)
  .build(); 


請參考以下範例示範以ButtonBuilder類別建立按鈕:


Image image = new Image(
  ButtonDemo.class.getResourceAsStream("images/dukeswing.gif"));

Scene scene = SceneBuilder.create()
  .width(250)
  .height(250)
  .root(
    FlowPaneBuilder.create()
      .hgap(5)
      .vgap(5)
      .padding(new Insets(5, 5, 5, 5))
      .alignment(Pos.CENTER)
      .children(
        // 設定一般按鈕
        ButtonBuilder.create()
          // 設定按鈕的文字
          .text("Plain Text")
          // 設定按鈕的最佳寬度
          .prefWidth(100)
          // 設定按鈕的最佳高度
          .prefHeight(20)
          .build(), 
        // 設定樣式按鈕
        ButtonBuilder.create()
          .text("Styling Text")
          // 設定按鈕文字的字型
          .font(Font.font("Verdana", 12))
          // 設定按鈕文字的顏色
          .textFill(Color.rgb(255, 0, 0, 1.0))
          .prefWidth(100)
          .prefHeight(20)
          .build(), 
        // 設定預設按鈕
        ButtonBuilder.create()
          .text("Default")
          // 設定按鈕是否為預設按鈕
          .defaultButton(true) 
          .prefWidth(100)
          .prefHeight(20)
          .build(), 
        // 設定取消按鈕
        ButtonBuilder.create()
          .text("Cancel")
          // 設定按鈕是否為取消按鈕
          .cancelButton(true) 
          .prefWidth(100)
          .prefHeight(20)
          .build(), 
        // 設定圖像按鈕
        ButtonBuilder.create()
          .text("Image Button")
          // 設定按鈕所使用的圖像
          .graphic(new ImageView(image))
          // 設定圖像與文字間的相對位置
          .contentDisplay(ContentDisplay.TOP) 
          .prefWidth(100)
          .build(), 
        // 設定非作用中按鈕
        ButtonBuilder.create()
          .text("Disable Button")
          .graphic(new ImageView(image))
          .contentDisplay(ContentDisplay.TOP) 
          .prefWidth(100)
          // 設定非作用中按鈕
          .disable(true)
          .build()
      )
      .build()
  )
  .build();
...


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

[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 8.0 API Specification.
[6] Java Platform, Standard Edition 8 API Specification.

© Chia-Hui Huang

2014年3月17日 星期一

JavaFX遊戲程式設計

花了三個月編寫的「JavaFX遊戲程式設計」,今天終於完成了,可以好好休息一下,章節包括:

1. JavaFX
2. 事件
3. 繪圖
4. 特殊效果
5. 動畫
6. 井字遊戲(Tic Tac Toe)
7. 拼圖(Puzzle)
8. 曲棍球(Hockey)
9. 孔明棋(Peg Solitaire)
10. 黑白棋(Reversi)
11. 西洋跳棋(Checker)
12. 翻翻樂(Matching Card)
13. 新接龍(Free Cell)
14. 俄羅斯方塊(Tetris)
15. 踩地雷(Mine Sweeper)
16. 數獨(Sudoku)

附錄:Java Web Start

© Chia-Hui Huang