ภาษาจาวาจะจัดเตรียมโปรแกรมที่พร้อมให้เรานำไปใช้งานโดยนำไปผนวกรวมเป็นส่วนหนึ่งของโปรแกรมของเรา โดยที่เราไม่จำเป็นต้องเสียเวลาในการเขียนโปรแกรมเพื่อทำงานเหล่านั้นขึ้นมาเอง เพื่อให้สามารถพัฒนาโปรแกรมได้ง่ายขึ้นและเร็วขึ้น เราเรียกชุดของโปรแกรมเหล่านี้ว่าไลบรารี่ (library) หรือในภาษาจาวาจะเรียกว่า Java API (Java Application Programing Interface)
ในการสร้างส่วนติดต่อกับผู้ใช้งานแบบ GUI (Graphic User Interface) เราจะใช้ Java API ที่ชื่อ JavaFX แต่ใน JDK ของ Oracle รวมถึง OpenJDK ของหลายๆค่ายที่เดินตามแนวทางมาตรฐานของ Oracle จะไม่มี JavaFX ใน JDK ตั้งแต่เวอร์ชั่น 9 เป็นต้นไป ดังนั้นเราต้องผนวกเอา JavaFX API เข้ามาเอง เช่น ดาวน์โหลด JavaFX API และเพิ่มเข้าไปในไลบรารี่ของโครงงานใน IntelliJ IDEA หรือเลือกใช้ JDK ซึ่งผนวกเอา Java FX ไว้ใน JDK ให้แล้วอย่าง BellSoft Lebrica JDK
การเพิ่ม JavaFX API ใน IntelliJ IDEA
สำหรับผู้ที่ไม่ได้เลือกใช้ JDK ของ BellSoft Lebrica JDK จะต้องเพิ่ม JavaFX API ใน IntelliJ IDEA โดยสามารถดาวน์โหลดได้ที่ https://gluonhq.com/products/javafx/ และเลือกดาวน์โหลดตามระบบปฎิบัติการของตนเอง และขยายไฟล์ออกมา
ไปที่ Project Structure
เลือก Global Libraries คลิกที่ + และเลือก Java
เลือกไฟล์ jar ของ JavaFX ที่ดาวน์โหลดมา และคลิก OK
เลือกชื่อโครงงานและคลิก OK
จากนั้นเลือก OK
การสร้างโครงการ Java FX
ในโปรแกรม IntelliJ IDEA เวอร์ชั่น 2021.2.2 ขั้นตอนการสร้างโครงการโดยใช้ Java FX จะบังคับใช้เครื่องมืออัตโนมัติที่ใช้ในการสร้าง (build) และจัดการโครงการ (project management) เช่น Maven หรือ Gradle ดังตัวอย่างด้านล่าง
อย่างไรก็ตามการสร้างไฟล์ .jar จากโครงการที่สร้างด้วยวิธีนี้มีปัญหาไม่สามารถนำไปใช้งานได้ ซึ่งเว็บของ INtelliJ IDEA เองก็แนะนำให้ใช้ build tool ตัวอื่น ดังนั้นเราจะสร้างโครงการ Java FX เพื่อการเรียนรู้ด้วยวิธีการเดียวกันกับการสร้างโครงการ Java ทั่วไปแต่ยังคงไว้ซึ่งประโชน์ของเฟรมเวิร์ค Java FX ไว้ดังนี้
1. สร้างไฟล์ Main.java และเขียนโปรแกรมตามตัวอย่างด้านล่าง โดยเลือก Projects และกดปุ่ม New Project
เลือก Java ตรวจสอบความถูกต้องของ JDK เวอร์ชั่น และกดปุ่ม Next
เลือก Create project from template และกดปุ่ม Next
ตั้งชื่อโครงการและกดปุ่ม Finish
เขียนโปรแกรมตามตัวอย่างลงไฟล์ Main.java
package com.company;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource(“sample.fxml”));
primaryStage.setTitle(“Hello World”);
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
2. สร้างไฟล์ Controller.java โดยเลือกเมนู File > New > Java Class และตั้งชื่อไฟล์ว่า Controller (ไม่ต้องใส่ .java) และเขียนโปรแกรมตามตัวอย่างด้านล่าง
package com.company;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class Controller {
@FXML
private Label welcomeText;
@FXML
protected void onHelloButtonClick() {
welcomeText.setText(“Welcome to JavaFX Application!”);
}
}
3. สร้างไฟล์ sample.fxml โดยเลือกเมนู File > New > File และตั้งชื่อไฟล์ว่า sample.fxml และเขียนโปรแกรมตามตัวอย่างด้านล่าง
<?xml version=”1.0″ encoding=”UTF-8″?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<VBox alignment=”CENTER” spacing=”20.0″ xmlns:fx=”http://javafx.com/fxml”
fx:controller=”com.company.Controller”>
<padding>
<Insets bottom=”20.0″ left=”20.0″ right=”20.0″ top=”20.0″/>
</padding>
<Label fx:id=”welcomeText”/>
<Button text=”Hello!” onAction=”#onHelloButtonClick”/>
</VBox>
และเมื่อเราลอง run โปรแกรม จะได้ผลลัพธ์ดังตัวอย่างด้านล่าง
การสร้างแอพพลิเคชั่น JavaFX ด้วยวิธีการนี้เราสามารถ build ด้วยขั้นตอนปรกติใน IntelliJ IDEA และสามารถนำไปใช้งานได้ เพียงแต่เครื่องที่จะนำไปใช้งานจะต้องติดตั้ง BellSoft Lebrica JDK และกำหนดให้เป็นจาวาตัวหลักของเครื่อง ซึ่งไม่น่าจะกระทบกับโปรแกรมอื่นที่พัฒนามาจากภาษาจาวาเพราะส่วนที่เป็น JavaSE API จะเหมือนกันตามมาตรฐานของ OpenJDK
จากตัวอย่างด้านล่างเป็นการทดสอบโดยสำเนาไฟล์ jar มาที่ไดเร็คทอรี่ home และลองเรียกใช้งาน
สำหรับผู้ที่ใช้ JDK ที่ไม่ใช่ BellSoft Lebrica JDK อาจจะพบข้อผิดพลาดในขั้นตอนการทดลอง run ใน IntelliJ IDEA จากตัวอย่างด้านล่าง เป็นข้อผิดพลาดว่าโปรแกรมไม่รู้จักไลบรารี่ของ JavaFX
การแก้ไขเราจะต้องเพิ่ม JavaFX API ในไลบรารี่ของโครงงาน โดยไปที่ Project Structure
เลือก Project และตรวจสอบว่า Language Level สอดคล้องกับ SDK ที่ใช้
เลือก Global Libraries คลิกขวาที่ JavaFX เลือก Add to Modules…
เลือกโครงงานที่ต้องการเพิ่ม JavaFX API และเลือก OK
เลือก OK
จะเห็น JavaFX API ถูกเพิ่มมาใน class path ของโครงงาน
และเมื่อเราลอง run โปรแกรมจะพบข้อผิดพลาด
การแก้ไขจะต้องสร้างไฟล์ module-info.java โดย คลิกขวาที่ src เลือก New และเลือก module-info.java
ระบุการเรียกใช้ JavaFX API ตามตัวอย่างด้านล่าง สำหรับคำสั่ง opens ให้ระบุชื่อแพคเกจ หากไม่มีแพคเกจต้องสร้างแพคเกจขึ้นมาโดยไปที่ src > New > Package > ตั้งชื่อแพคเกจ > และย้ายไฟล์ .java และ .fxml ไปไว้ภายใต้แพคเกจ
และในการใช้งานจะต้องสำเนาไดเร็คทอรี่ของ JavaFX ไปยังเครื่องที่ต้องใช้งานด้วย และเรียกใช้งานด้วยคำสั่ง java -jar –module-path </javafx/bin> –add-modules=javafx.controls,javafx.fxml program.jar ดังตัวอย่างด้านล่าง
การทำงานของ JavaFX
JavaFX ถูกออกแบบมาให้รองรับการสร้างแอพพลิเคชั่นในรูปแบบ MVC (Module View Control) โดย MVC หมายถึงการออกแบบโปรแกรมแบบแยกส่วนของการทำงาน (module) การแสดงผล (view) และการควบคุม (control) ออกจากกัน ดังนั้นส่วนของโปรแกรมที่เป็นส่วนติดต่อกับผู้ใช้งาน (User Interface – UI) หรือก็คือคลาสที่สร้างจาก JavaFX ควรจะแยกจากส่วนของโปรแกรมที่ใช้จัดการกับข้อมูลหรือก็คือคลาสที่สร้างจาก Java และทั้งสองส่วนจะเชื่อมต่อกันผ่านทางตัวควบคุม (controller)
โครงงาน JavaFX จะประกอบด้วยไฟล์ที่เกี่ยวข้อง 3 ไฟล์คือ Main.java Controller.java และ sample.fxml
ไฟล์ sample.fxml หรืออาจจะเป็นชื่ออื่นที่เราตั้งขึ้นมาก็ได้แต่ต้องมีนามสกุลเป็น fxml คือไฟล์ที่ใช้กำหนดเลย์เอ้าท์ของหน้าจอที่เป็นส่วนติดต่อกับผู้ใช้งานของโปรแกรมที่เราจะสร้างขึ้นมา เช่น ขนาดหน้าต่าง มีปุ่ม มีกล่องข้อความอยู่ตรงไหนบ้าง เป็นต้น ไฟล์ sample.fxml จะถูกเขียนด้วยรูปแบบภาษา xml ซึ่งเราสามารถใช้เครื่องมืออย่าง scene builder มาช่วยในการสร้างไฟล์นี้ได้โดยไม่ต้องยุ่งยากกับการเขียนด้วยรูปแบบภาษา xml เอง ถ้าเปรียบเทียบกับรูปแบบ MVC ไฟล์นี้ก็คือส่วนติดต่อกับผู้ใช้งาน (User Interface – UI) ซึ่งก็คือ view นั่นเอง
ไฟล์ Controller.java คือ ตัวควบคุม (controller) ที่ทำหน้าที่เชื่อมต่อส่วนติดต่อกับผู้ใช้งานและคลาสต่างๆที่ใช้จัดการกับข้อมูล โดยรูปแบบการเชื่อมต่อจะเป็นแบบดำเนินการตามเหตุการณ์ที่เกิดขึ้น (event driven) เช่น เมื่อผู้ใช้งานกดปุ่มแล้วให้ไปเรียกใช้งานเมธอดใดจากคลาสใด เป็นต้น
ส่วนไฟล์ Main.java ก็คือไฟล์ที่ใช้จัดการการใช้งานโปรแกรมที่สร้างด้วย JavaFX ตั้งแต่เริ่มต้นใช้งานจนกระทั่งปิดโปรแกรม ไฟล์ Main.java จะ extends มาจากคลาส Application และมีเมธอดสำคัญที่ใช้จัดการการใช้งานโปรแกรมของเราคือ เมธอด init() เมธอด start() เมธอด stop() และ เมธอด launch() โดยเมธอด start() เป็นเมธอดแบบแอบแสตรกซึ่งเราต้องโอเวอร์ไรด์เมธอด start() เพื่อสร้างหน้าจอตามที่เราต้องการตามตัวอย่างด้านล่าง สำหรับเมธอดอื่นเราสามารถโอเวอร์ไรด์เมธอดเพื่อให้ดำเนินการตามที่เราต้องการได้เช่นกัน
การทำงานของ Main.java Controller.java และ sample.fxml
เริ่มต้นที่ไฟล์ Main.java เมื่อเราสั่ง run โปรแกรม เมธอด launch() ซึ่งถูกเรียกใช้งานภายใต้เมธอด main() จะทำหน้าที่ในการเริ่มต้นการทำงานของโปรแกรมและจะจบการทำงานก็ต่อเมื่อเราปิดโปรแกรม
เมื่อเราเรียกเมธอด launch() แล้วเมธอด init() จะถูกเรียกขึ้นมาทำงานในลำดับถัดไป จากนั้นตามมาด้วยเมธอด start() และเมื่อเราปิดโปรแกรมเมธอด stop() จะถูกเรียกใช้งาน
เมธอด start() เป็นเมธอดแบบแอบแสตรกซึ่งก็คือการบังคับให้เราโอเวอร์ไรด์เพื่อสร้างส่วนติดต่อผู้ใช้งานในแบบที่เราต้องการ เมธอด start() จะใชัออบเจกต์จากคลาส Stage เป็นพารามิเตอร์ก็คือการสั่งสร้างหน้าต่างหลักของโปรแกรมของเรา อาจจะจำง่ายๆว่าเสตจ (stage) ก็คือเวทีหลักที่ออบเจกต์ต่างๆจะมาแสดงก็ได้ จากภาพด้านล่าง กรอบสีแดงแสดงพี้นที่ของเสตจ
อาจจะมีข้อสงสัยว่าออบเจกต์ Stage ที่ใช้เป็นพารามิเจอร์นั้นมาจากไหน ออบเจกต์ Stage ตัวนี้ JavaFX สร้างขึ้นมาให้เราเอง แต่หลังจากนี้หากเราต้องการสร้างออบเจกต์ Stage อื่นๆเพื่อใช้งานจะต้องสร้างเองและจะไม่เป็นเวทีหลัก
บรรทัดถัดมาจะเป็นการเรียกใช้ส่วนติดต่อผู้ใช้งานจากไฟล์ sample.fxml
ซึ่งเมื่อเราตามไปดูเนื้อหาในไฟล์ sample.fxml จะมีบรรทัดที่ระบุว่า fx:controller ซึ่งจะบอกว่าไฟล์ fxml ไฟล์นี้จะใช้ไฟล์ Controller ไฟล์ใด ซึ่ง sample.Controller หมายถึงไฟล์ sample.fxml ใช้ไฟล์ Controller.java
เนื่องจากไฟล์ Controller.java จะถูกใช้ในการเชื่อมต่อระหว่างส่วนติดต่อผู้ใช้งานกับคลาสต่างๆเพื่อให้ดำเนินการตามที่เราต้องการ แต่เรายังไม่ได้สร้างส่วนติดต่อผู้ใช้งานดังนั้นไฟล์นี้จึงยังไม่ถูกใช้งาน
กลับมาที่ Main.java กลุ่มของบรรทัดต่อมาคือการกำหนดค่าให้กับเสตจหรือ หน้าต่างของโปรแกรมของเราผ่านทางเมธอดของออบเจกต์ Stage เช่น เมธอด setTitle() เป็นการกำหนดชื่อที่แสดงบนหน้าต่าง เมธอด setScene() เป็นการกำหนดสิ่งที่เราจะแสดง และสั่งแสดงหน้าต่างด้วยเมธอด show()
เพื่อความเข้าใจมากขึ้นเกี่ยวกับการใช้ซีน (scene) ให้นึกถึงว่าเสตจหรือเวทีจริงๆแล้วมันเป็นพื้นที่ว่างเพื่อรองรับการแสดง และการแสดงแต่ละอย่างก็จะมีฉากหรือเรียกว่าซีนของตนเองมาวางบนเวที ดังนั้นเมื่อเราต้องการเปลี่ยนรูปแบบหน้าจอไปตามการใช้งานที่ออกแบบไว้ก็คือการเปลี่ยนซีนไม่ใช่การเปลี่ยนเสตจ และภายในซีนประกอบด้วยเลย์เอ้าท์ (layout) ซึ่งเป็นการแบ่งส่วนของหน้าจอ และออบเจกต์ต่างๆ เช่น ปุ่ม กล่องข้อความ เป็นต้น
เมื่อเราสั่ง run โปรแกรมจะเห็นว่าหน้าต่างจะมีไอคอนสำหรับ ย่อ ขยายและปิดหน้าต่างมาให้ด้วยอยู่แล้วซึ่งก็คือฟิลด์และเมธอดที่มีอยู่แล้วในคลาส Stage นั่นเอง
เปรียบเทียบการใช้ fxml และการใช้คำสั่งในการออกแบบหน้าจอ
ถึงแม้เราจะใช้ไฟล์ fxml ในการสร้างส่วนติดต่อกับผู้ใช้งาน แต่จริงๆแล้ว
ออบเจกต์ต่างๆที่เราจะกำหนดใน fxml ก็คือคลาสต่างๆที่ Java FX API มีให้เราใช้งานนั่นเอง เพียงแต่ถูกเรียกใช้งานในรูปแบบ xml ซึ่งง่ายต่อการทำความใจมากกว่าการเขียนโปรแกรมด้วยภาษาจาวาแบบปรกติ อย่างไรก็ตามเพื่อเปรียบเทียบให้เห็นการใช้งานของทั้ง 2 แบบเราจะใช้ตัวอย่างด้านล่างในการทำความเข้าใจ โดยในตัวอย่างจะเป็นการพิมพ์ปุ่มชื่อ button ในหน้าต่างหลักของโปรแกรมดังนี้
ในกรณีที่เราใช้ fxml เราจะบอกกับเมธอด start ว่าเราใช้ fxml ไฟล์ใดดังตัวอย่างในบรรทัดที่ 15
และใน fxml ไฟล์เราจะสร้างเลย์เอาท์ (Gridpane) ขึันมาและสร้างปุ่ม(Button)ไว้ในเลย์เอาท์
ในกรณีที่เราเขียนโปรแกรมแบบภาษาจาวา เราไม่ต้องระบุว่าใช้ไฟล์ fxml ไฟล์ใด และใช้คำสั่งในการสร้างเลย์เอาท์และสร้างปุ่มแทน สังเกตว่าการสร้างเลย์เอาท์คือการสร้างออบเจกต์ Gridpane และการสร้างปุ่มคือการสร้างออบเจกต์ Button และเราต้องบอกให้ตัวแปรภาษารู้ว่าเราจะเพิ่มปุ่มในเลย์เอาท์ด้วยเมธอด add() ของคลาส Gridpane
ทั้ง 2 วิธีให้ผลลัพธ์เหมือนกัน แต่ในกรณีที่หน้าจอเรามีความซับซ้อนจะเห็นว่าการใช้ fxml จะเข้าใจง่ายกว่า
สังเกตุว่าเมื่อเรากำหนดออบเจกต์เลย์เอาท์หรือออบเจกต์ตัวควบคุม เช่น ปุ่ม กล่องข้อความ เป็นต้น จะมีการ impot คลาสของออบเจกต์นั้นๆเข้ามาเหมือนกันเพียงแต่รูปแบบแตกต่างออกไป
เลย์เอาท์
ในการสร้างส่วนติดต่อกับผู้ใช้งาน เราจะสร้างออบเจกต์เสตจขึ้นมาซึ่งเราอาจจะมองได้ว่าออบเจกต์เสตจก็คือหน้าต่างของแอพพลิเคชั่นนั่นเอง จากนั้นเราจึงสร้างซีนอยู่ในเสตจอีกทีหนึ่ง ซึ่งซีนก็คือการสร้างออบเจกต์ที่เป็นเลย์เอาท์และสร้างตัวควบคุมซึ่งเป็นออบเจกต์ที่เป็นส่วนติดต่อกับผู้ใช้ เช่น ปุ่ม , กล่องข้อความ เป็นต้น ไว้ภายในออบเจกต์เลย์เอาท์อีกทีหนึ่ง เราสามารถใช้เลย์เอาท์ซ้อนในเลย์เอาท์เพื่อสร้างหน้าจอที่ซับซ้อนขึ้นได้ ด้วยความสามารถของคลาสที่ภาษาจาวามีมาให้ เราสามารถสนใจแต่เรื่องการออกแบบหน้าจอและเหตุการณ์ที่จะเกิดขึ้นกับแต่ละตัวควบคุมได้โดยไม่ต้องสนใจเรื่องปลีกย่อยเช่นแต่ละตัวควบคุมจะแสดงผลอย่างไรเมื่อมีการหดหรือขยายหน้าจอเพราะคลาสที่มีมาให้จัดการเรื่องเหล่านี้ให้หมดแล้ว
การกำหนดเลย์เอาท์และตัวควบคุมในไฟล์ fxml จะเป็นรูปแบบ xml คือใช้แทก (tag) เป็นตัวกำหนดตามรูปแบบดังนี้
<ชื่อออบเจกต์ คุณสมบัติ คุณสมบัติ …> แทกอื่นๆ </ชื่อออบเจกต์>
โครงสร้างการออกแบบโดยใช้เลย์เอาท์และตัวควบคุมจะเป็นไปตามตัวอย่างด้านล่าง
<ชื่อเลย์เอาท์ในระดับบน>
<ชื่อเลย์เอาท์ที่ซ้อนอยู่ด้านใน>
<ชื่อตัวควบคุม>
</ชื่อตัวควบคุม>
</ชื่อเลย์เอาท์ที่ซ้อนอยู่ด้านใน>
.
.
.
</ชื่อเลย์เอาท์ระดับบน>
เราสามารถดูคุณสมบัติ (attribute) ของแต่ละแทกที่สามารถใช้ได้สำหรับแต่ละชนิดของออบเจกต์โดยกด spaceเพื่อให้ IntelliJ IDEA แสดงตัวเลือกมาให้เราดู ดังตัวอย่าง
นอกจากคุณสมบัติที่สามารถระบุได้ในแทกเองแล้ว ยังมีคุณสมบัติที่ต้องกำหนดเป็นแทกแยกออกมาต่างหากรวมถึงตัวควบคุมอื่นๆที่เราสามารถกำหนดได้ภายใต้แทก ซึ่งเราสามารถดูรายการทั้งหมดได้เช่นกันโดยพิมพ์แค่ < เพื่อให้ IntelliJ IDEA แสดงตัวเลือกมาให้เราดู ดังตัวอย่าง
เลย์เอาท์ที่มีให้เราใช้งานประกอบด้วย Grid Pane, Anchor Pane, Stack Pane, HBox, VBox, Flow Pane, Tile Pane,Border Pane และ Dialog Pane
Border Pane เป็นเลย์เอาท์พื้นฐานที่พบเห็นได้ในแอพพลิเคชั่นทั่วไป โดยในเลย์เอาท์จะแบ่งออกเป็นส่วนบน-ล่าง-ซ้าย-ขวา-กลาง ดังตัวอย่างด้านล่างเป็นการกำหนดตัวควบคุมแบบข้อความ (Label) ในแต่ละส่วนของเลย์เอาท์ Border Pane
Grid Pane เป็นเลย์เอาท์ที่สามารถแบ่งพื้นที่ในเลย์เอาท์ออกเป็นตาราง (grid) โดยเลขที่แถวและคอลัมภ์จะเริ่มต้นจาก 0 ค่าตั้งต้นของ grid pane คือไม่มีตารางหรือมองว่าพื้นที่ทั้งหมดคือ 1 ช่องที่เกิดจาก 1 แถวและ 1 คอลัมภ์ก็ได้คือแถวที่ 0 และคอลัมภ์ที่ 0
ตัวอย่างด้านล่างเป็นการใช้เลย์เอาท์แบบ Grid Pane ในการแสดงตัวควบคุมแบบข้อความ (Label) โดยกำหนดแถวและคอลัมภ์ตามที่ต้องการ
HBox pane และ VBox pane เป็นเลย์เอาท์ที่เมื่อเราเอาตัวควบคุมไปใส่ในเลย์เอาท์แลัวตัวควบคุมจะเรียงกันไปตามแนวนอนสำหรับ HBox pane และ เรียงกันในแนวตั้งสำหรับ VBox pane โดยมากเราไม่ใช้ HBox pane และ VBox pane เป็นเลย์เอาท์หลักแต่มักจะใช้ซ้อนในเลย์เอาท์อื่นๆอีกทีเพื่อเป็น
กรอบสำหรับรวบรวมปุ่มหรือเมนู ดังตัวอย่างด้านล่าง จะเห็นว่าเราเอาแทก VBox และ HBox ซ้อนไว้ภายในแทก Border Pane ในส่วน bottom และ left และกำหนดตัวควบคุมแบบปุ่ม (Button) ซึ่งจะวางตัวเรียงกันตามคุณสมบัติของเลย์เอาท์
Flow Pane เป็นเลย์เอาท์ในลักษณะเดียวกันกับ HBox Pane และ VBox Pane แต่ตัวควบคุมจะสามารถปรับเปลี่ยนตำแหน่ง เช่น ย้ายมาขึ้นบรรทัดใหม่ได้ตามขนาดของหน้าต่าง (wrap) โดยเราสามารถกำหนดการปรับเปลี่ยนตามแนวตั้งหรือแนวนอนได้จากการกำหนดคุณสมบัติ orientation จากตัวอย่างด้านล่างเป็นการซ้อน Flow Pane ในกรอบด้านขวาของ Border Pane และกำหนดการปรับเปลี่ยนตามแนวตั้ง สังเกตุการปรับปลี่ยนจากการย่อหรือขยายหน้าต่าง
Tile Pane เป็นเลย์เอาท์ที่มีลักษณะคล้าย Grid Pane เพียงแต่ช่องแต่ละช่อง (grid) จะถูกปรับให้เท่ากันตามขนาดของตัวควบคุมที่กินพื้นที่มากที่สุด จากตัวอย่างด้านล่างสังเกตุว่าพื้นที่ของตัวควบคุมแบบข้อความ (Label) จะถูกกันไว้เท่าๆกันตามข้อความที่ยาวที่สุด
Stack Pane เป็นเลย์เอาท์ที่จะวางตำแหน่งงตัวควบคุมในตำแหน่งเดียวกันซ้อนๆกันไปโดยตัวควบคุมที่ถูกกำหนดไว้ก่อนจะอยู่ล่างสุด จากตัวอย่างจะเห็นว่าตัวควบคุมแบบข้อความ(Label) จะซ้อนทับกันที่ตำแหน่งเดียวกัน
ออบเจกต์ตัวควบคุม
การติดต่อกับผู้ใช้งานประกอบไปด้วยการแสดงผลและการรับคำสั่งซึ่งทั้งหมดนี้จะถูกทำผ่านออบเจกต์ที่เป็นตัวควบคุม เช่น เราใช้ป้ายข้อความ (Label) ในการแสดงผล ใช้ปุ่ม (Button) เพื่อให้กดเพื่อดำเนินการ ใช้ปุ่มตัวเลือก (Radio Button) เพื่อให้เลือกข้อมูลจากกลุ่มของข้อมูลที่กำหนด เป็นต้น
ตัวควบคุมแบบป้ายข้อความ (Label) เป็นตัวควบคุมที่ใช้แสดงข้อความ ลักษณะการใช้งานคือ
<Label text=”text to show on label”/> ในกรณีที่คำสั่งจบในแทกเดียว หรือ
<Label text=”text with another tag under”>
<other tag1/>
<other tag2>
</other tag2>
</Label>
ตัวควบคุมแบบปุ่ม (Button) เป็นตัวควบคุมที่ใช้รับคำสั่งจากผู้ใช้งานโดยการกดปุ่มเพื่อดำเนินกิจกรรมอื่นๆต่อไป ลักษณะการใช้งานคือ
<Button text=”text on button”/> ในกรณีที่คำสั่งจบในแทกเดียว หรือ
<Button text=”with other tag”>
<other tag1/>
<other tag2>
</other tag2>
</Button>
ตัวควบคุมแบบปุ่มตัวเลือก (Radio Button) เป็นตัวควบคุมที่มี 2 สถานะคือถูกเลือก (on) หรือไม่ถูกเลือก (off) โดยจะใช้ในกรณีที่มีหลายทางเลือกแต่ให้ผู้ใช้เลือกอย่างใดอย่างหนึ่งเท่านั้น โดยตัวควบคุมแบบปุ่มตัวเลือกจะต้องใช้ร่วมกับความสามารถของ ToggleGroup ซึ่งจะถูกใช้งานผ่านคุณสมบัติ fx:define เพื่อจัดกลุ่มให้กับปุ่มตัวเลือก เพื่อให้ปุ่มตัวเลือกสามารถถูกเลือกได้เพียงปุ่มเดียวในกลุ่มของปุ่มตัวเลือกเดียวกัน หากเราไม่ใช้ความสามารถของ ToggleGroup จะทำให้ทุกปุ่มตัวเลือกถูกเลือกได้พร้อมกันซึ่งผิดวัตถุประสงค์ของการใช้ปุ่มตัวเลือก ลักษณะการใช้งานคือ
<fx:define>
<ToggleGroup fx:id=”groupname”/>
</fx:define>
<RadioButton text=”RadioButton1″ toggleGroup=”$groupname”
selected=”true”/>
<RadioButton text=”RadioButton2″ toggleGroup=”$groupname” />
<RadioButton text=”RadioButton3″ toggleGroup=”$groupname” />
สังเกตุว่าเราใช้ fx:id ในการตั้งชื่อกลุ่มและอ้างชื่อกลุ่มในแต่ละแทกของปุ่มตัวเลือกที่อยู่ในกลุ่มเดียวกันโดยการอ้างชื่อกลุ่มจะต้องนำด้วย $ และใช้คุณสมบัติ selected=”true” เพื่อกำหนดค่าตั้งต้นที่ถูกเลือก
ตัวควบคุมแบบกล่องกาเครื่องหมาย (Check Box) เป็นตัวควบคุมที่มีสถานะได้ 3 สถานะคือ ไม่แน่นอน (indeterminate) ถูกเลือก (on) และไม่ถูกเลือก (off) ซึ่งโดยมากสถานะไม่แน่นอนจะไม่ถูกใช้ หากต้องการใช้สถานะไม่แน่นอน เราจะกำหนดให้สถานะไม่แน่นอนเป็นค่าตั้งต้นได้เท่านั้นโดยกล่องจะแสดงเครื่องหมาย – และหากต้องการให้ค่าตั้งต้นเป็นสถานะถูกเลือกให้กำหนด selected=”true” ลักษณะการใช้งานคือ
<CheckBox text=”text” />
ตัวอย่างการใช้งาน
ตัวควบคุมช่องข้อความ (Text Field) และช่องรหัสผ่าน (Password Field) จะถูกใช้ในกรณีที่เราต้องการให้ผู้ใช้งานป้อนข้อความเพื่อนำไปดำเนินการต่อ โดยช่องรหัสผ่านจะไม่แสดงข้อความที่ป้อนเข้ามา ลักษณะการใช้งาน
<TextField />
<PasswordField/>
ตัวอย่างการใช้งาน
ตัวควบคุมแบบกล่องคำสั่งผสม (Combo Box) เป็นตัวควบคุมที่เสนอทางเลือกให้ผู้ใช้เลือก ลักษณะการใช้งาน
<ComboBox>
<items>
<FXCollections fx:factory=”observableArrayList”>
<String fx:value=”Option One”/>
<String fx:value=”Option Two”/>
<String fx:value=”Option Three”/>
</FXCollections>
</items>
</ComboBox>
ตัวควบคุมแบบกล่องทางเลือก (Choice Box) เป็นตัวควบคุมที่ใช้งานในลักษณะเดียวกันกับตัวควบคุมแบบกล่องคำสั่งผสม (Combo Box) แต่จะมีเครื่องหมายกำกับตัวเลือกที่ถูกเลือกไว้ ลักษณะการใช้งาน
<ChoiceBox>
<items>
<FXCollections fx:factory=”observableArrayList”>
<String fx:value=”Option One”/>
<String fx:value=”Option Two”/>
<String fx:value=”Option Three”/>
</FXCollections>
</items>
</ChoiceBox>
ตัวควบคุมแบบตัวเลื่อน (Slider) เป็นตัวควบคุมที่ให้ผู้ใช้เลื่อนตัวชี้เพื่อเลือกค่าตัวเลขที่ต้องการ ลักษณการใช้งาน
<Slider min=”0″ max=”100″
showTickLabels=”true”
showTickMarks=”true”
minorTickCount=”10″
snapToTicks=”true” />
โดยคุณสมบัติ min และคุณสมบัติ max เป็นการกำหนดช่วงค่าที่ต้องการ กำหนดคุณสมบัติ showTickLabels เป็น true เพื่อแสดงค่าตัวเลข กำหนดคุณสมบัติ showTickMarks เป็น true เพื่อแสดงขีดย่อย กำหนดคุณสมบัติ minorTickCount เพื่อกำหนดจำนวนขีดย่อย และ กำหนดคุณสมบัติ snapToTicks เป็น true เพื่อให้การเลื่อนไปหยุดอยู่ตรงขีดที่ใกล้ที่สุด
ตัวควบคุมแบบตัวหมุน (Spinner) เป็นตัวเลือกที่ให้ผู้ใช้งานคลิกลูกศรขึ้น-ลงเพื่อเลือกค่าที่ต้องการ (จึงเรียกว่าหมุนไปตามที่เลือก) ลักษณะการใช้งาน
<Spinner min=”0″ max=”20″/>
โดยคุณสมบัติ min และคุณสมบัติ max เป็นการกำหนดช่วงค่าที่ต้องการ
ตัวควบคุมการเลือกสี (Color Picker) เป็นตัวควบคุมที่ช่วยให้เราเลือกค่าสีได้อย่างง่ายๆ ลักษณะการใช้งาน
<ColorPicker/>
ตัวควบคุมการเลือกวันที่ (Date Picker) เป็นตัวควบคุมที่ช่วยให้เราเลือกค่าวันที่ได้อย่างง่ายๆ ลักษณะการใช้งาน
<DatePicker/>
ตัวควบคุมแบบบานหน้าต่าง (Titled Pane) เป็นตัวควบคุมที่ใช้ในการแบ่งส่วนหน้าจอเพื่อหดหรือขยายหน้าจอส่วนนั้นได้ ลักษณะการใช้งาน
<TitledPane text=”ชื่อของหน้าต่าง”>
<ออบเจกต์อื่นที่ตอ้งการบรรจุในหน้าต่าง/>
<ออบเจกต์อื่นที่ตอ้งการบรรจุในหน้าต่าง/>
</TitledPane>
ตัวอย่างการใช้งาน
ตัวควบคุมแบบหีบเพลง (Accordion) เป็นการเอาตัวควบคุมแบบบานหน้าต่าง (Titled Pane) มารวมกลุ่มกันโดยผู้ใช้งานสามารถเปิดดูได้ครั้งละหน้าต่างเท่านั้น ลักษณะการใช้งาน
<Accordion>
<panes>
<TitledPane text=”Title One”>
<Label text=”This is information in pane one.”/>
</TitledPane>
<TitledPane text=”Title Two”>
<Label text=”This is information in pane two.”/>
</TitledPane>
<TitledPane text=”Title Three”>
<Label text=”This is information in pane three.”/>
</TitledPane>
</panes>
</Accordion>
ตัวอย่างการใช้งาน
ตัวควบคุมเมนู (MenuBar) เป็นตัวควบคุมที่ใช้สร้างเมนูตาม ตัวอย่างด้านล่าง ลักษณะการใช้งาน
<MenuBar>
<Menu text=”File”>
<items>
<MenuItem text=”New”/>
<SeparatorMenuItem/>
<MenuItem text=”Open”/>
</items>
</Menu>
</MenuBar>
ตัวอย่างการใช้งาน
ตัวควบคุมแบบแสดงรายการ (ListView) เป็นตัวควบคุมที่ใช้แสดงข้อมูลเป็นรายการ เช่น รายการของที่ต้องการซื้อ รายการอีเมล์ในกล่องจดหมายเข้าเป็นต้น การใช้งานประกอบด้วย 2 ส่วนคือการกำหนดตัวควบคุมแบบแสดงรายการในไฟล์ fxml และการสั่งแสดงรายการไนไฟล์ Controller.java
ลักษณะการใช้งานคือ
<ListView/> ในกรณีที่คำสั่งอยู่ในแทกเดียว หรือ
<ListView>
</ListView>
จากตัวอย่างด้านล่าง เรากำหนดตัวควบคุมแบบแสดงรายการ (ListView) ในไฟล์ fxml กำหนด id ด้วยคุณสมบัติ fx:id เป็น listview1 และกำหนดความกว้างตั้งต้นของพื้นที่ด้วยคุณสมบัติ minWidth โดยกำหนดเป็น 500 พิกเซล จากนั้นสร้างตัวแปร listview1 เป็นชนิด ListView ในไฟล์ Controller.java เพื่อใช้อ้างอิงถึงตัวควบคุมในไฟล์ fxml เพื่อที่จะได้สั่งให้แสดงข้อมูลทั้งหมดใน ArrayList ด้วยเมธอด setAll และกำหนดให้สามารถเลือกได้ทีละรายการด้วยเมธอด setSelectionMode และในการใช้งานตามตัวอย่างด้านล่าง เราใช้เมธอด getSelectedItems เพื่อแสดงสิ่งที่ถูกเลือกในตัวควบคุมแบบพื้นที่ข้อความ (TextArea)
เนื่องจากการใช้งานตัวควบคุมแบบแสดงรายการต้องใช้ความสัมพันธ์ระหว่างไฟล์ fxml และไฟล์ Controller.java ซึ่งสามารถทำความเข้าใจเพิ่มเติมได้จากหัวข้อการจัดการเหตุการณ์ที่เกิดจากการใช้งาน (Event Handling)
จากตัวอย่างด้านบน การรับรู้การถูกเลือกของรายการมาจากการคลิกเมาส์ ซึ่งถ้าเราลองเลื่อนรายการโดยใช้ปุ่มลูกศรจะเห็นว่าข้อมูลทางฝั่งตัวควบคุมแบบพื้นที่ข้อความไม่ได้เปลี่ยนแปลงตาม ถึงแม้ว่าเราจะกดปุ่ม Enter ด้วยก็ตาม ถ้าเราต้องการจะให้ทุกเหตุการณ์ถูกดำเนินการอย่างถูกต้อง เราจะต้องสร้างการรับรู้สำหรับหลายเหตุการณ์ เช่น เมื่อคลิกเมาส์ เมื่อกด enter เป็นต้น แต่ถ้าเราพิจารณาอย่างละเอียดจะพบว่าไม่ว่าจะคลิกเมาส์หรือเลื่อนด้วยปุ่มลูกศร สิ่งที่เกิดขึ้นคือแถบที่ไฮไลท์จะเลื่อนไปยังรายการที่ต้องการ
Java API มีคลาสแบบอินเตอร์เฟสที่ดักจับการเปลี่ยนแปลงที่เกิดขึ้นคือคลาสแบบอินเตอร์เฟส ChangeListener (https://docs.oracle.com/javase/8/javafx/api/javafx/beans/value/ChangeListener.html) ซึ่งค่าที่เปลี่ยนในที่นี้คือการเปลี่ยนค่าของรายการ ดังนั้นเราสามารถรับรู้การเปลี่ยนแปลงที่เกิดขึ้นไม่ว่าจะเกิดจากจากการคลิกเมาส์หรือเลื่อนด้วยปุ่มลูกศรได้ด้วยการใช้งานคลาสนี้ โดยมีรูปแบบการใช้งานดังตัวอย่างด้านล่าง
การใช้งานคลาสแบบอินเตอร์เฟส ChangeListener จะเป็นการสร้าง anonymous class และกำหนดให้เราต้องโอเวอร์ไรด์เมธอด changed เพื่อดำเนินการตามที่ต้องการ
ตัวควบคุมแบบเมนูตามบริบท (Context Menu) เป็นตัวควบคุมรายการที่แสดงเมนูเมื่อเราคลิกขวาที่รายการที่ต้องการ เหมาะกับการใช้งานที่ต้องการให้เกิดขึ้นเฉพาะเจาะจงกับแต่ละรายการ จากตัวอย่างด้านล่างเป็นการใช้งานตัวควบคุมแบบเมนูตามบริบทอย่างง่ายๆ เราจะสร้างตัวควบคุมแบบเมนูตามบริบทในไฟล์ Controller.java
เริ่มจากกำหนดตัวแปรของตัวควบคุมแบบเมนูตามบริบท ตามที่ไฮไลท์ในภาพ
จากนัันสร้างออบเจกต์ของตัวควบคุมแบบเมนูตามบริบทในเมธอด initialize และกำหนดเมนูที่เราต้องการให้แสดง ตามที่ไฮไลท์ในภาพ
จากนั้นกำหนดกิจกรรมที่ต้องการให้ดำเนินการเมื่อมีการคลิกที่เมนูโดยใช้คลาส EventHandle ในการดักจับ event ที่เกิดขึ้น ตามที่ไฮไลท์ในภาพ
และกำหนดให้รายการรู้จักกับตัวควบคุมแบบเมนูตามบริบท ตามที่ไฮไลท์ในภาพ
เมื่อเราเลือกรายกรที่ต้องการ แล้วคลิกขวาของเมาส์ จะปรากฏเมนูลอยขึ้นมาให้เราให้ใช้งาน จากตัวอย่างเราคลิกที่ Menu One ซึ่งจะพิมพ์ผลลัพธฺในหน้าต่างคอนโซล
การจัดการเหตุการณ์ที่เกิดจากการใช้งาน (Event Handling)
JavaFX ส่งเสริมการใช้งานในรูปแบบ MVC (Module View Control) ซึ่งส่วนของ View คือการออกแบบส่วนติดต่อผู้ใช้งานด้วยไฟล์ fxml ประกอบไปด้วยตัวควบคุมแบบต่างๆ สำหรับในหัวข้อนี้เราจะมาดูในส่วนของ Control ซึ่งคือการสร้างเมธอดเพื่อจัดการกับเหตุการณ์ที่เกิดจากการใช้งานของผู้ใช้ผ่านการกระทำกับตัวควบคุม เช่น เมื่อผู้ใช้งานป้อนข้อความ หรือเมื่อผู้ใช้งานกดปุ่ม เป็นต้น เพื่อบอกให้โปรแกรมจะต้องทำอะไรต่อไป
การจะทำให้ส่วน view หรือไฟล์ fxml ทำงานร่วมกับส่วน control หรือไฟล์ Controller.java ได้นั้นเราต้องทำให้ไฟล์ Controller.java รู้จักตัวควบคุมในไฟล์ fxml และเราต้องทำให้ไฟล์ fxml รู้จักเมธอดในไฟล์ Controller.java
การทำให้ไฟล์ Controller.java รู้จักตัวควบคุมในไฟล์ fxml ทำได้โดยกำหนด id ให้กับตัวควบคุมในไฟล์ fxml โดยใช้คุณสมบัติ fx:id เช่น
<Label fx:id=”label1> , <Button fx:id=”button1”> และ <TextArea fx:id=” textarea1”> โดย id จะต้องไม่ซ้ำกัน จากนั้นสร้างตัวแปรสำหรับตัวควบคุมแต่ละตัวที่ต้องการใช้งานในไฟล์ Controller.java โดยชนิดของตัวแปรเป็นชนิดเดียวกันกับตัวควบคุมที่ต้องการอ้างอิงและชื่อตัวแปรต้องเหมือนกับชื่อที่กำหนดในคุณสมบัติ fx:id ของตัวแปรนั้นๆ และแต่ละตัวแปรต้องกำกับด้วย @FXML ตามตัวอย่างด้านล่าง
@FXML
TextField textfield1;
@FXML
Button button1;
@FXML
TextArea textarea1;
เมื่อไฟล์ Controller.java รู้จักตัวควบคุมในไฟล์ fxml แล้ว เราสามารถเรียกใช้งานเมธอดต่างๆของออบเจกต์ตัวควบคุมผ่านตัวแปรได้ จากตัวอย่างด้านล่างเราเรียกใช้เมธอด setText ของออบเจกต์ TextField ผ่านตัวแปร textarea1
@FXML
public void onButtonClicked(ActionEvent actionEvent) { //ดักจับปุ่ม
if (actionEvent.getSource().equals(button1)){
printResult(); //เรียกเมธอดแสดงข้อความ
}
}
@FXML
public void printResult(){ //รับข้อมูลมาแสดง
textarea1.setText(textfield1.getText());
}
สำหรับการทำให้ไฟล์ fxml รู้จักเมธอดที่เราสร้างขึ้นในไฟล์ Controller.java เพื่อที่เมื่อผู้ใช้มีการใช้งาน เช่น ป้อนข้อความ กดปุ่ม เป็นต้น จะได้เรียกใช้งานเมธอดที่เรากำหนดไว้ได้อย่างถูกต้อง ทำได้โดยการกำหนดชื่อเมธอดของเราในคุณสมบัติที่เป็นกิจกรรมของตัวควบคุม เช่น onAction หรือ onKeyRelease เป็นต้น ขึ้นอยู่กับกิจกรรมที่ตัวควบคุมรองรับ ตัวอย่างเช่น บอกให้ออบเจกต์ตัวควบคุมแบบปุ่มเรียกเมธอด onButtonClicked() เมื่อมีการกดปุ่มโดยการกำหนดในคุณสมบัติ onAction เช่น <Button fx:id=”button1” onAction=”#onButtonClicked”> โดยต้องมี # นำหน้าชื่อเมธอดใน Controller.java ที่ต้องการเรียกใช้งาน
ตัวอย่างการใช้งาน
จากตัวอย่างด้านบนจะเห็นว่ามีการใช้งานคลาส ActionEvent เพื่อตรวจจับการกดปุ่มโดยในเมธอด onButtonClicked เราสร้างตัวแปรชนิด ActionEvent เป็นพารามิเตอร์ ดังตัวอย่าง
คลาส ActionEvent เป็นคลาสที่ใช้สร้างออบเจกต์ของเหตุการณ์ (event) ที่เกิดขึ้นจากออบเจกต์ตัวควบคุม เช่น เมื่อเรากดปุ่ม ตัวควบคุมแบบปุ่มจะสร้างออบเจกต์ชนิด ActionEvent ขึ้นมา
จากตัวอย่างด้านบน เมื่อเราสร้างตัวแปรชนิด ActionEvent ขึ้นมาและเรียกใช้เมธอด getsource เพื่อชี้ไปยังออบเจกต์ที่เป็นผู้ผลิตเหตุการณ์ขึ้นมา จากนั้นใช้เมธอด equals ซึ่งเป็นเมธอดที่สืบทอดมาจากคลาส Object เพื่อเปรียบเทียบออบเจกต์ที่ถูกชี้ด้วยเมธอด getsource กับออบเจกต์ตัวควบคุมที่ถูกชี้โดยตัวแปร button1 โดยผลลัพธ์ของนิพจน์นี้จะเป็นจริงเมื่อออบเจกต์ที่ถูกชี้โดยเมธอด getsource และออบเจกต์ที่ถูกชี้โดยตัวแปร button1 เป็นออบเจกต์ตัวเดียวกัน
เมื่อตรวจสอบได้แล้วว่าเป็นออบเจกต์ตัวเดียวกันจึงเรียกใช้งานเมธอด printResult ที่เราสร้างขึ้นมาซึ่งจะอ้างอิงถึงออบเจกต์ตัวควบคุมแบบ TextArea ที่ถูกชี้โดยตัวแปร textarea1 เพื่อกำหนดค่าข้อความที่จะนำไปแสดงโดยใช้เมธอด setText ซึ่งข้อความนั้นได้มาจากการอ้างอิงถึงออบเจกต์ตัวควบคุมแบบ TextField ที่ถูกชี้โดยตัวแปร textfield1 และใช้เมธอด getText ในการเข้าถึงข้อความนั้น
ปรับแต่งเลย์เอาท์และตัวควบคุมด้วย CSS และรูปภาพ
JavaFX รองรับการใช้ CSS (Cascading Style Sheets) ตามมาตรฐาน W3C CSS เวอร์ชั่น 2.1 และยังมีส่วนเพิ่มขยายสำหรับ Java FX การใช้ CSS ทำให้เราสามารถปรับแต่งหน้าตาของออบเจกต์เลย์เอาท์และออบเจกต์ตัวควบคุมได้ตามต้องการ การใช้ CSS ทำได้โดยการเพิ่มคุณสมบัติ style ไปในแทกที่ต้องการและกำหนดค่าสำหรับแต่ละเรื่อง เช่น กรอบ (border) เป็นต้น โดยรูปแบบในการใช้งานคือ
Style=“-fx-สิ่งที่ต้องการปรับแต่ง-เรื่องที่ต้องการปรับแต่ง:ค่าที่ต้องการ”
ดังตัวอย่างด้านล่าง เป็นการกำหนดให้ออบเจกต์ Label ในบรรทัดที่ 6 มีกรอบสีน้ำเงินด้วยการกำหนด style=”-fx-border-color:BLUE”
หากเราใช้งาน IntelliJ IDEA ที่เป็นเวอร์ชั่นเสียตังค์ (ultimate version) จะแสดงตัวเลือก CSS ต่างๆมาให้เราเลือกสอดคล้องกับออบเจกต์ที่เราใช้งาน ดังตัวอย่างด้านล่าง
แต่ใน IntelliJ IDEA เวอร์ชั่นฟรี ( community version) จะไม่มีความสามารถดังกล่าว แต่เราสามารถหาข้อมูลเพิ่มเติมว่ามีคุณสมบัติอะไรให้ใช้ได้บ้างจาก https://docs.oracle.com/javafx/2/api/javafx/scene/doc-files/cssref.html
นอกจากการปรับแต่งด้วย CSS แล้วเรายังสามารถเพิ่มรูปภาพในออบเจกต์ได้โดยเราสามารถดาวน์โหลดไลบรารี่รูปภาพมาเพิ่มเข้าในโครงการของเรา
จากตัวอย่างด้านล่าง เราดาวน์โหลดไลบรารี่รูปภาพจาก http://www.java2s.com/example/jar/j/download-jlfgr10jar-file.html
จากนั้นขยายไฟล์ไปไว้ที่ภายใต้ไดเร็คทอรี่ของโครงการ
สังเกตุว่าชื่อไฟล์จะแสดงในไดเร็คทอรี่ทางซ้ายมือ
เราเพิ่มไลบารี่ดังกล่าวในโครงการโดยไปที่ โครงการ คลิกขวา เลือก Open Module Setting
จากหน้าต่าง Project Structure ภายใต้เมนู Project Setting คลิกที่ Module จากนั้นเลือกโครงการที่ต้องการ และคลิก + ทางด้านขวาของหน้าต่าง และเลือก 1. JARs or directories…
เลือกไฟล์ที่ดาวน์โหลดมาแล้วกดปุ่ม OK
เลือก Classes แล้วกด OK
ขั้นตอนจะกลับมาที่หน้าจอ Project Structure สังเกตว่าไฟล์ของเราจะถูกเพิ่มอยู่ในรายการแล้ว ให้กดปุ่ม Apply และ OK ตามลำดับ
เมื่อกลับมาที่หน้าจอที่ใช้เขียนโปรแกรมจะเห็นว่าไลบรารี่รูปภาพถูกเพิ่มมาที่ด้านซ้าย
จากนั้นให้เพิ่มบรรทัด requires ตามด้วยชื่อไฟล์ในไฟล์ module-info.java ดังตัวอย่างด้านล่าง
เราสามารถเพิ่มรูปภาพได้โดยใช้แทก <graphic> <ImageView> และ <Image> ตามตัวอย่างด้านล่างเป็นการเพิ่มรูปภาพที่ด้านหน้าของ Label “BelowBelowBelow”
ลดความผิดพลาดด้วยการควบคุมผู้ใช้งาน
ในการใช้งานแอพพลิเคชั่น ข้อผิดพลาดที่เกิดขึ้นส่วนหนึ่งมาจากการใช้งานอย่างไม่ถูกต้องของผู้ใช้งาน ดังนั้นเราจึงต้องควบคุมการใช้งานให้เป็นไปตามที่เราต้องการ เช่น การทำให้ปุ่มที่ต้องกดเพื่อดำเนินการต่อไม่สามารถกดได้ตราบใดผู้ใช้งานยังไม่ได้ป้อนข้อมูลในฟิลด์ที่กำหนด หรือการกำหนดค่าตั้งต้นไว้เสมอเพื่อป้องกันในกรณีที่ผู้ใช้ไม่ได้เลือกหรือป้อนข้อมูลตามที่กำหนด เป็นต้น โดยเราสามารถใช้คุณสมบัติของแต่ละออบเจกต์ตัวควบคุมมาช่วยในการควบคุมผู้ใช้งาน เช่น การกำหนดค่าตั้งต้นที่ถูกเลือกในตัวควบคุมแบบปุ่มตัวเลือกด้วยคุณสมบัติ selected=”true” ตามตัวอย่างด้านล่าง จะเห็นว่าตัวควบคุมแบบปุ่มตัวเลือกอันแรกจะถูกเลือกไว้แล้วเมื่อเปิดหน้าต่างขึ้นมา
ตัวอย่างด้านล่างเป็นการกำหนดให้ตัวควบคุมแบบปุ่มไม่สามารถกดได้เป็นค่าตั้งต้นด้วยคุณสมบัติ disble=”true” จากนั้นสร้างเมธอดเพื่อตรวจสอบการป้อนข้อมูลและเชื่อมต่อเมธอดโดยการกำหนดเมธอดในแทกตัวควบคุมแบบช่องข้อความ โดยเมื่อมีข้อมูลปุ่มจึงจะสามารถแสดงให้กดได้
นอกจากการควบคุมผู้ใช้งานผ่านการใช้งานออบเจกต์ควบคุมแต่ละตัวแล้ว เราอาจจะควบคุมความถูกต้องของข้อมูลในขั้นตอนสุดท้ายที่ผู้ใช้งานกดปุ่มเพื่อดำเนินการต่อก็ได้ โดยตรวจสอบค่าที่ต้องการทั้งหมด แจ้งขอผิดพลาดที่พบให้กับผู้ใช้งาน และไม่ดำเนินการต่อจนกว่าผู้ใช้งานจะแก้ไขข้อผิดพลาดดังกล่าว
จากตัวอย่างด้านบนเรากำหนดในแทกตัวควบคุมแบบปุ่มให้ไม่สามารถกดได้โดยกำหนดในคุณสมบัติตั้งแต่ตอนสร้างตัวควบคุบแบบปุ่ม แต่ในกรณีที่เรามีตัวควบคุมหลายแบบ หรือ หน้าจอเราซับซ้อน การกำหนดค่าเริ่มต้นโดยตรงที่แทกอาจจะลำบากในการติดตามแก้ไขในภายหลัง เราสามารถกำหนดค่าเริ่มต้นสำหรับทุกตัวควบคุมได้โดยการกำหนดเมธอด Initialize ในไฟล์ Controller.java ซึ่ง JavaFX จะเรียกใช้งานเมธอด Initialize ทุกครั้งที่เริ่มสร้างส่วนติดต่อผู้ใช้งาน จากตัวอย่างด้านล่าง เรากำหนดค่าตั้งต้นให้ตัวควบคุมแบบกล่องข้อความแสดงข้อความที่กำหนด และกำหนดค่าตั้งต้นให้ตัวควบแบบปุ่มไม่สามารถกดได้
เอางานที่ทำเสร็จช้าไปซ่อนไว้ข้างหลัง
หลายๆคนคงเคยมีประสบการณ์ในการใช้งานแอพพลิเคชั่นแล้วหน้าจอไม่ยอมให้เราทำอะไรและต้องรอสักพักจึงจะยอมให้เราใช้งานต่อได้ อาการเช่นนี้เกิดจากส่วนติดต่อผู้ใช้งาน (UI Thread) รอผลลัพธ์จากทำงานที่ส่งต่อให้กับเมธอด (event handler) ทีเรากำหนด เช่น เมื่อเราคลิกปุ่มให้อ่านข้อมูลจากฐานข้อมูล ซึ่งฐานข้อมูลอาจจะใหญ่หรือการเชื่อมต่ออาจจะช้า ทำให้หน้าจอแสดงเครื่องหมายให้เรารอการประมวลผลและเราไม่สามารถใช้งานอะไรต่อได้เป็นต้น ดังนั้นเพื่อหลีกเลี่ยงปัญหาดังกล่าว เราสามารถกำหนดให้กิจกรรมที่คาดว่าจะใช้เวลานานถูกย้ายไปทำงานเบื้องหลัง (run in another Thread in background) และปล่อยให้ส่วนติดต่อผู้ใช้งานสามารถโต้ตอบกับเราต่อไปได้ โดยเราจะส่งโปรแกรมส่วนที่ต้องการให้ทำงานเบื้องหลังไปให้ออบเจกต์ Runnable ดูแล โดยมีรูปแบบการใช้ดังนี้
Runnable task = new Runnable() {
@Override
public void run(){
// โปรแกรมที่คาดว่าจะใช้เวลานานในการทำงาน
}
};
new Thread (task).start();
ในกรณีที่โปรแกรมส่วนที่เราส่งไปให้ออบเจกต์ Runnable ดูและมีการเรียกใช้งานออบเจกต์ตัวควบคุมด้วย เราจะต้องห่อหุ้มส่วนนั้นด้วยเมธอด Platform.runlater เพราะ JavaFX ยอมให้ติดต่อกับออบเจกต์ตัวควบคุมผ่านส่วนติดต่อผู้ใช้งาน (UI Thread) เท่านั้น ในขณะที่โปรแกรมที่เราให้ออบเจกต์ Runnable เป็นการเอาส่วนของโปรแกรมดังกล่าวไปรันในเธรดอื่น การเรียกใช้ เมธอด Platform.runlater ดังตัวอย่างด้านล่าง
Runnable task = new Runnable() {
@Override
public void run(){
// โปรแกรมที่คาดว่าจะใช้เวลานานในการทำงาน
Platform.runlater(new Runnable(){
@Override
public void run(){
// โปรแกรมที่อ้างถึงออบเจค์ตัวควบคุม
}
});
}
};
new Thread (task).start();
การใช้งาน Dialog Pane
Dialog Pane เป็นเลย์เอาท์ที่แตกต่างจากเลย์เอาท์อื่นตรงที่ dialog pane ไม่ได้อยูในซีนเดียวกันกับเลย์เอาท์อื่น ตัวอย่างของ dialog pane คือหน้าต่างที่ pop-up ขึ้นมาเพื่อให้เราทำกิจกรรมต่างๆ เช่น ยืนยัน กรอกข้อมูล เป็นต้น ดังนั้น dialog pane จะต้องมีไฟล์ fxml และ ไฟล์ Controller ของตัวเอง และเราจะมองว่า dialog pane เป็นหน้าต่างลูกของหน้าต่างหลักที่เรียกใช้งาน dialog pane
เริ่มจากการสร้างไฟล์ fxml โดยไปที่ไดเร็กทอรี่ของแพคเกจ คลิกขวาเลือก New > FXML file ตั้งชื่อไฟล์และกดปุ่ม OK
IntelliJ IDEA จะสร้างไฟล์ xml ให้ตามตัวอย่างด้านล่าง สังเกตุว่าบรรทัดที่ 11 จะอ้างถึงไฟล์ Controller ที่ยังไม่มีจึงเป็นสีแดง ซึ่งเราต้องสร้างคลาส Controller ขึ้นมาสำหรับ diaolog pane
การสร้างคลาส Controller ทำได้โดยไปที่ไดเร็กทอรี่ของแพคเกจ คลิกขวาเลือก New > Java Class ตั้งชื่อไฟล์และกด enter
IntelliJ IDEA จะสร้างไฟล์ java class ให้ตามตัวอย่างด้านล่าง
จากนั้นให้กำหนดชื่อไฟล์ Controller ที่สร้างขึ้นมาในไฟล์ fxml ดังตัวอย่าง
ด้านล่าง และแก้ไขเลย์เอาท์เป็น <Dialog Pane> ตามตัวอย่างด้านล่าง
ตัวอย่างต่อไปนี้จะเป็นการใช้ dialog pane อย่างง่ายเพื่อให้เห็นภาพการกำหนดค่าต่างๆเพื่อให้ dialog pane สามารถทำงานได้ โดยในตัวอย่างจะเป็นการสร้างหน้าต่างหลักที่มีแค่เมนูเพื่อเรียกใช้งาน dialog pane และใน dialog pane มีฟิลด์เพื่อรับข้อความและวันที่และมีปุ่ม OK และ Cancle เพื่อให้ผู้ใช้งานเลือกเพื่อพิมพ์ข้อมูลในคอนโซลหรือยกเลิก ตามตัวอย่างด้านล่าง
เริ่มจากในไฟล์ sample.fxml ของหน้าต่างหลักในบรรทัดที่ 14 เรากำหนดเมนู Open Dialog (เพื่อเรียกใช้ dialog pane) และกำหนดให้เรียกใช้งานเมธอด showDialog (onAction=”showDialog”) เมธอดดังกล่าวจะถูกกำหนดไว้ในไฟล์ Controller.java ซึ่งเป็นไฟล์ controller ของหน้าต่างหลัก และกำหนด id ให้กับ Boder Pane ในบรรทัดที่ 9 (fx:id=”mainWindwos”) เพื่อไว้ใช้อ้างอิงในการเป็นหน้าต่างหลัก
ในไฟล์ newdialog.fxml ซึ่งเป็นไฟล์ที่กำหนดเลย์เอาท์ของ dialog pane เรากำหนดให้มีตัวควบคุมแบบกล่องรับข้อความ (Text Field) และตัวควบคุมการเลือกวันที่ (Date Picker)
จากนั้นเราจึงกำหนดการจัดการกับเหตุการณ์ (even handler) ของ dialog pane ในไฟล์ NewDialogController.java โดยในที่นี้เรากำหนดให้พิมพ์ข้อความในคอนโซลเมื่อมีการกดปุ่ม OK (สังเกตุว่าปุ่มไม่ได้ถูกกำหนดในไฟล์ newdialog.fxml)
ในไฟล์ Controller.java ซึ่งเป็นไฟล์ controller ของหน้าต่างหลัก เราสร้างเมธอด showDialog เพื่อเรียกใช้งาน dialog pane โดยในเมธอดประกอบด้วย
บรรทัดที่ 15 เป็นการสร้างออบเจกต์ Dialog ขึ้นมา
บรรทัดที่ 16 ใช้เมธอด initOwner เพื่อกำหนดหน้าต่างหลักให้ออบเจกต์ Dialog
บรรทัดที่ 17-18 กำหนดการแสดงข้อความที่ส่วนหัวของ dialog
บรรทัดที่ 19 เป็นการสร้างออบเจกต์ FXMLLoader ขึ้นมา
บรรทัดที่ 20 กำหนดไฟล์ fxml ให้กับออบเจกต์ FXMLLoader
บรรทัดที่ 22 กำหนดไฟล์ fxml ให้กับออบเจกต์ Dialog โดยอ้างอิงจากออบเจกต์ FXMLLoader
บรรทัดที่ 29-30 เป็นการกำหนดปุ่ม OK และ Cancel
บรรทัดที่ 31 เป็นการแสดง dialog
บรรทัดที่ 32-37 เป็นทางเลือกในการดำเนินการเมื่อกดปุ่ม
บรรทัดที่ 34-35 เป็นการเรียกใช้เมธอดที่กำหนดในไฟล์ NewDialogController.java
ในกรณีที่เราต้องการสร้าง dialog เพียงเพื่อต้องการยืนยันจากผู้ใช้งาน หรือเพื่อแจ้งข้อมูล เราสามารถใช้งาน dialog สำเร็จรูปที่ JavaFX มีมาให้ในรูปแบบคลาส Alert ซึ่งสามารถเลือกใช้ได้ 4 แบบคือ เพื่อยืนยัน (confirmation dialog) เพื่อแจ้งข้อผิดพลาด (error dialog) เพื่อแจ้งข้อมูล (information dialog) และเพื่อแจ้งเตือน (warning dialog) โดยเราสามารถกำหนดคุณสมบัติและกิจกรรมเมื่อมีการกดปุ่ม จากตัวอย่างด้านล่างแสดง dialog แบบต่างๆและให้แสดงข้อความที่หน้าต่างคอนโซลเมื่อมีการกดปุ่ม
เริ่มจากเพิ่มปุ่มในไฟล์ sample.fxml
กำหนดตัวแปรในไฟล์ Controller.java
กำหนดเมธอดที่จะถูกเรียกใช้เมื่อมีการกดปุ่ม
ตัวอย่างด้านล่างเป็น dialog ในแต่ละแบบเมื่อกดปุ่มตามที่กำหนด
การเรียกใช้ Scene Builder
จากการเรียนรู้ที่ผ่านมาจะเห็นว่าเราสร้างหน้าจอโดยใช้ไฟล์ fxml ซึ่งหากเป็นหน้าจอที่ซับซ้อนมากๆ การทำงานโดยตรงกับไฟล์ fxml จะค่อนข้างยาก เราสามารถใช้โปรแกรม Scene Builder เพื่อช่วยให้งานง่ายขึ้น และเราสามารถแยกงานการออกแบบหน้าจอออกไปให้ทีมออกแบบได้ง่ายขึ้น
เราสามารถดาวน์โหลดโปรแกรม Scene Builder ได้จาก https://gluonhq.com/products/scene-builder/#download
สำหรับแพคเกจเดเบี้ยน Linux deb หลังจากขยายไฟล์ออกมาแล้ว ให้เรียกโปรแกรมที่ data/opt/scenebuilder/bin/SceneBuilder.jar และเลือกเทมเพลทที่ต้องการ
หน้าจอของ SceneBuilder แบ่งออกเป็น ด้านบนเป็นเมนูต่างๆ ด้านซ้ายเป็นไลบรารี่ของออบเจกต์ให้เราเลือกมาวางในพื้นที่ทำงานตรงกลาง และด้านขวาเป็นคุณสมบัติของแต่ละออบเจกต์ประกอบด้วย property เช่น หน้าตา สี ข้อความที่แสดง layout เช่น ความกว้างของขอบ ระยะห่างจากออบเจกต์อื่น และ code เช่น เรียกเมธอดใดเมื่อคลิกเมาส์ เรียกเมธอดใดเมื่อมีการเปลี่ยนแปลง เป็นต้น
เราสามารถบันทึกเป็นไฟล์ fxml ได้โดยไปที่เมนู File > Save
เราสามารถเรียกใช้ Scene Builder จากใน IntelliJ IDEA ได้โดยคลิกขวาที่ไฟล์ fxml ในหน้าต่างโครงการและเลือก Open In SceneBuilder