Tutorial JavaFX 8 - komputasi.files.wordpress.com · 1 Tutorial JavaFX 8 (Bahasa Indonesia) Apr 19,...
Transcript of Tutorial JavaFX 8 - komputasi.files.wordpress.com · 1 Tutorial JavaFX 8 (Bahasa Indonesia) Apr 19,...
1
Tutorial JavaFX 8
(Bahasa Indonesia)
Apr 19, 2014 • Updated Mar 12, 2015
Pada 2012 saya telah menulis Seri Tutorial JavaFX 2 yang terperinci untuk murid-muridku.
Banyak orang dari penjuru dunia telah membaca tutorial ini dan memberi umpan balik yang
positif. Lalu saya memutuskan menulis ulang Tutorial JavaFX 2 untuk JavaFX 8 (baca
perubahan di Pembaharuan javaFX 8 - Apa yang baru).
Tutorial ini menuntun anda melalui merancang, pemrograman dan menggunakan aplikasi
alamat. Berikut adalah bentuk akhir aplikasi yang akan terlihat:
Apa yang akan anda pelajari
• Membuat dan memulai proyek JavaFX
• Menggunakan Scene Builder untuk mendesain antar muka pengguna
• Strukturisasi sebuah aplikasi dengan pola Model-Tampilan-Pengendali (Model-View-
Controller)
2
• Menggunakan ObservableListsuntuk secara otomatis memperbaharui antarmuka
pengguna
• Menggunakan TableView dan bereaksi terhadap perubahan seleksi pada tabel
• Membuat dialog ubah sesuai munculan untuk memperbaharui persons
• Validasi masukan pengguna
• Memberi gaya pada aplikasi JavaFX dengan CSS
• Memaksa data sebagai XML
• Menyimpan alamat berkas terakhir dibuka didalam preferensi pengguna
• Membuat bagan JavaFX untuk statistika
• Penyebaran aplikasi JavaFX sebagai paket asli
Ini cukup banyak!, jadi setelah kamu telah menyelesaikan seri tutorial-tutorial ini, kamu
seharusnya telah siap untuk membangun aplikasi canggih dengan JavaFX.
Bagaimana menggunakan tutorial ini
Ada dua cara untuk menggunakan tutorial ini:
• Jalur pelajari lebih banyak: Buat proyek JavaFX anda sendiri dari awal.
• Jalur cepat: Impor source kode dari bagian tutorial ke IDE (ini merupakan proyek yang
dibuat dengan Eclipse, tapi anda bisa menggunakan IDE lainnya seperti NetBeans
dengan sedikit modifikasi). Kemudian melalui tutorial untuk memahami kode tersebut.
Sekarang, saya harap anda akan bersenang-senang! dimulai dari Bagian 1 : Scene Builder.
3
Tutorial JavaFX 8 - Bagian 1: Scene Builder
Apr 19, 2014 • Updated Mar 12, 2015
Pembahasan dibagian 1
• Mengenal JavaFX
• Membuat dan memulai proyek JavaFX
• Menggunakan Scene Builder untuk desain antar muka pengguna
• Struktur dasar aplikasi menggunakan pola Model-View-Controller (MVC)
Prasyarat
• Java JDK 8 terbaru (termasuk JavaFX 8).
• Eclipse 4.4 atau lebih besar dengan e(fx)clipse plugin. Eclipse 4.3 atau lebih besar dengan
e(fx)clipse plugin dar website e(fx)clipse. Sebagai alternatif bisa menggunakan situs
pembaharuan untuk instalasi Eclipse.
• Scene Builder 8.0 (provided by Gluon because Oracle hanya memberikan ini dalam bentuk
code).
4
Konfigurasi Eclipse
Kita perlu untuk memberitahu Eclipse untuk menggunakan JDK 8 dan dimana dapat
ditemukannya Scene Builder:
1. Buka preferensi Eclipse dan arahkan ke Java | Installed JREs.
2. Klik Add..., pilih Standard VM dan pilih Directory instalasi JDK 8 anda.
3. Hapus JRE atau JDK lainnya, sehingga JDK 8 menjadi baku.
4. Arahkan ke Java | Compiler. atur Compiler compliance level to 1.8.
5
5. Arahkan ke preferensi JavaFX. Rincikan alamat pengeksekusi Scene Builder.
Link-link membantu
Anda mungkin ingin menandai tautan berikut:
• Java 8 API - JavaDoc kelas-kelas baku java
• JavaFX 8 API - JavaDoc untuk kelas-kelas JavaFX
• ControlsFX API - JavaDoc untuk kontrol-kontrol tambahan JavaFX ControlsFX project
• Oracle's JavaFX Tutorials - Tutorial resmi JavaFX oleh Oracle
Sekarang, mari kita mulai!
Buat Sebuah Proyek JavaFX
Di Eclipse (dengan e(fx)clipse telah terpasang) arahakan ke File | New | Other... dan pilih
JavaFX Project. Tetapkan nama proyek (contoh AddressApp) dan klik Finish.
Hapus paket application berserta isinya jika telah dibuat secara otomatis.
Membuat Paket-Paket
Tepat dimulai dari awal kita akan mengikuti prinsip-prinsip desain perangkat lunak yang bagus.
Satu prinsip yang sangat penting adalah Model-Tampilan-Pengendali (Model-View-
Controller). Berdasarkan pada hal ini kita memisahkan kode kita menjadi tiga bagian dan
membuat masing-masing paket (klik kanan pada src-folder, New... | Package):
• ch.makery.address - berisi umumnya kelas-kelas pengendali (=logika bisnis)
6
• ch.makery.address.model - memuat kelas-kelas model
• ch.makery.address.view - memuat tampilan-tampilan
Catatan: Paket tampilan kita juga akan memuat beberapa kontroller yang secara langsung
terhubung pada tampilan. Sebuat saja view-controllers.
Buat berkas Tataletak FXML
Ada dua cara untuk membuat antarmuka pengguna. Entah menggunakan berkas XML atau
langsung memprogram semuanya didalam Java. Carilah di internet, dan anda pasti menemukan
2 cara ini. Kita akan menggunakan XML (berakhiran .fxml) untuk kebanyakan bagian. Saya
menemukan ini cara yang lebih bersih untuk menjaga controller dan view terpisah secara
mandiri. Selanjutnya, kita dapat menggunakan Scene Builder untuk memperbaharui XML,
yang berarti kita tidak akan langsung berhubungan dengan XML.
Klik kanan pada paket ch.makery.address.view dan buat berkas baru bernama
PersonOverview.
7
Mendesain dengan Scene Builder
Note: Jika anda tidak berhasil mengikuti, unduh kode sumber dari bagian-bagian tutorial ini, dan
cobalah dengan fxml yang telah disertakan.
(jika Scene builder tidak dapat terbuka, pergi ke "Window | Prefrences | JavaFX" dan atur
Klik kanan pada PersonOverview.fxml dan pilih Open with Scene Builder. Sekarang anda
seharusnya melihat Scene Builder dengan sebuah AncherPane (terlihat di bawah Hierarchy
pada sisi kiri).
8
1. Pilih Anchor Pane di Hierarchy adan sesuaikan ukuran di bawah pada Layout (pada sisi
kanan):
2. Tambah sebuah Split Pane (Horizontal Flow) tutorial/part1/anchor-pane-size.png
9
1. Tambah sebuah Split Pane (Horizontal Flow) dengan menyeret dari Library kepada
area utama. Klik kanan pada Split Pane pada tampilan Hierarchy dan pilih Fit to Parent.
3. Seret TableView (dari Controls) ke bagian sisi kiri dari SplitPane. Pilih TableView
(bukan sebuah kolom) dan atur kendala Tataletak pada TableView. Didalam
AnchorPane kamu akan bisa selalu mengatur jangkar pada 4 perbatasan.
4. Pada menu Preview | Show Preview in Window untuk melihat apakah memiliki perilaku
yang tepat. Coba ubah ukuran jendela. Ukuran TableView seharusnya mengikuti
perubahan jendela, sebagaimana telah di kaitkan pada perbatasan.
10
5. Mengganti teks kolom (pada Properties) ubah ke "First Name" dan "Last Name".
6. PilihTableView lalu constrained-resize untuk Column Resize Policy (pada Properties).
Ini memastikan bahwa kolom-kolom akan selalu mengisi ruang kosong yang ada.
7. Tambahkan Label pada sisi kanan dengan teks "Person Details" (petunjuk: gunakan
pencarian untuk menemukan Label). Sesuaikan tataletak menggunakan jangkar.
8. Tambahkan GridPane pada sisi kanan, pilih dan sesuaikan tataletak menggunakan
jangkar (atas, kanan, dan kiri).
11
9. Tambah label-label berikut ke sel-sel.
Catatan: Untuk menambah pada GridPane pilih sebuah baris(akan berwarna kuning),
klik kanan dan pilih "Add Row".
10. Tambah sebuah ButtonBar di bawah, lalu tambah 3 tombol pada bar. Sekarang, atur
jangkar (kanan dan bawah) pada ButtonBar sehingga itu tetap pada tempat yang tepat.
11. Sekarang anda seharusnya melihat sesuatu seperti berikut. Gunakan menu Preview
untuk mencoba perilaku perubahan ukuran.
12
Buat Aplikasi Utama
Kita perlu FXML baru untuk tata letak akar yang akan berisi sebuah palang menu dan
membungkus PersonOverview.fxml yang baru kita buat.
1. Buat dokumen FXML didalam paket view bernama RootLayout.fxml. Kali ini pilih
BorderPane sebagai elemen akar.
2. Buka RootLayout.fxml didalam Scene Builder.
13
3. Ubah ukuran BorderPane dengan Pref Width diatur ke 600 dan Pref Height diatur ke
400.
4. Tambahkan MenuBar ke celah teratas. Kita tidak akan menerapkan fungsi menu saat
ini.
Kelas Utama JavaFX
Sekarang kita perlu membuat main java class yang menjalankan aplikasi kita dengan
RootLayout.fxml dan menambah PersonOverview.fxml pada posisi tengah.
14
1. Klik kanan pada proyek dan pilih New | Other... kemudian JavaFX Main Class.
2. Kita akan panggil MainApp dan menaruh ini didalam paket Controller
ch.makery.address (Catatan: ini adalah paket induk dari view dan anak paketmodel).
Kelas MainApp.java dihasilkan dari Application dan berisi dua metode. Ini adalah struktur
dasa yang kita perlukan untuk memulai Aplikasi JavaFX. Bagian terpenting untuk kita adalah
metode start(Stage primaryStage).Ini secara otomatis dipanggil dari dalam metode main.
15
Seperti yang anda lihat metode start(...) menerima Stage sebagai parameter. Grafik berikut
menjelaskan struktur dari tiap aplikasi JavaFX:
Image Source: http://www.oracle.com
Seperti sebuah drama pertunjukan: Stage adalah penampung utama yang biasanya adalah
Windowdengan sebuah batasan, dan beserta tombol-tombol mengecilkan, membesarkan, dan
tutup. Didalam Stage anda menambah Scene yang tentunya dapat diganti oleh Scene lainnya.
Didalam Scene simpul seperti AnchorPane, TextBox, dan lain-lain ditambahkan.
Buka MainApp.java dan ganti kode yang ada dengan :
package ch.makery.address;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
16
public class MainApp extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
@Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("AddressApp");
initRootLayout();
showPersonOverview();
}
/**
* Initializes the root layout.
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
17
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Shows the person overview inside the root layout.
*/
public void showPersonOverview() {
try {
// Load person overview.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/PersonOverview.fxml"));
AnchorPane personOverview = (AnchorPane) loader.load();
// Set person overview into the center of root layout.
rootLayout.setCenter(personOverview);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Returns the main stage.
* @return
*/
public Stage getPrimaryStage() {
return primaryStage;
}
18
public static void main(String[] args) {
launch(args);
}
}
Berbagai macam komentar seharusnya dapat memberikan anda beberapa petunjuk mengenai
apa yang dilakukan.
Jika anda menjalankan aplikasi sekarang, anda seharusnya melihat sesuatu seperti tangkapan
layar seperti di awal mula artikel.
Permasalahan yang sering terjadi
Jika JavaFX tidak dapat menemukan berkas fxml yang telah dispesifikasikan, anda mungkin
mendapatkan pesan error seperti:
java.lang.IllegalStateException: Location is not set.
Untuk mengatasi ini, periksa duakali nama dari berkas fxml tersebut. Jika masih tidak berfungsi,
unduh kode sumber dari tutorial ini, dan coba dengan fxml terlampir.
Jika masih tidak dapat berfungsi, unduh kode sumber dari tutorial ini dan cobalah dengan fxml yang
telah termasuk.
Berikutnya ?
Di Tutorial bagian 2 kita akan menambah beberapa data fungsi pada AddressApp.
19
Tutorial JavaFX 8 - Bagian 2: Model dan
TableView
Apr 23, 2014 • Updated Mar 12, 2015
Pembahasan di bagian 2
• Membuat kelas model
• Menggunakan kelas model didalam ObservableList
• Menampilkan data di TableView menggunakan Controllers
Membuat kelas model
Kita perlu membuat sebuah kelas model untuk menampung informasi mengenai orang di buku
alamat kita. Tambah sebuah kelas ke paket model (ch.makery.address.model) bernama
Person. Kelas Person akan mempunyai beberapa variable instansi untuk name, address, dan
birthday. Tambah kode berikut ke kelas.
20
Person.java
package ch.makery.address.model;
import java.time.LocalDate;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
* Model class for a Person.
*
* @author Marco Jakob
*/
public class Person {
private final StringProperty firstName;
private final StringProperty lastName;
private final StringProperty street;
private final IntegerProperty postalCode;
private final StringProperty city;
private final ObjectProperty<LocalDate> birthday;
/**
* Default constructor.
*/
public Person() {
21
this(null, null);
}
/**
* Constructor with some initial data.
*
* @param firstName
* @param lastName
*/
public Person(String firstName, String lastName) {
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
// Some initial dummy data, just for convenient testing.
this.street = new SimpleStringProperty("some street");
this.postalCode = new SimpleIntegerProperty(1234);
this.city = new SimpleStringProperty("some city");
this.birthday = new
SimpleObjectProperty<LocalDate>(LocalDate.of(1999, 2, 21));
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public StringProperty firstNameProperty() {
return firstName;
22
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public StringProperty lastNameProperty() {
return lastName;
}
public String getStreet() {
return street.get();
}
public void setStreet(String street) {
this.street.set(street);
}
public StringProperty streetProperty() {
return street;
}
public int getPostalCode() {
return postalCode.get();
}
23
public void setPostalCode(int postalCode) {
this.postalCode.set(postalCode);
}
public IntegerProperty postalCodeProperty() {
return postalCode;
}
public String getCity() {
return city.get();
}
public void setCity(String city) {
this.city.set(city);
}
public StringProperty cityProperty() {
return city;
}
public LocalDate getBirthday() {
return birthday.get();
}
public void setBirthday(LocalDate birthday) {
this.birthday.set(birthday);
}
public ObjectProperty<LocalDate> birthdayProperty() {
return birthday;
24
}
}
Penjelasan
• Dengan JavaFX sangat lazim untuk menggunakan Properties untuk semua bidang kelas
model. Sebuah Property memungkinkan kita, sebagai contoh, otomatis di beritau
ketikalastName atau variabel lainnya berubah. Ini membantu untuk menjaga tampilan
sinkron dengan data. Untuk mempelajari lebih lanjut mengenai Properties baca Using
JavaFX Properties and Binding.
• LocalDate, tipe yang telah kita gunakan untuk birthday, adalah bagian dari Date and Time
API for JDK 8.
Daftar dari Persons
Data utama yang dikelola aplikasi kta adalah orang. Mari buat daftar untuk object Person
didalam kelas MainApp. Semua kelas kontrol akan dapat mengakses daftar utama didalam
MainApp.
ObservableList
Kita berkerja dengan kelas tampilan JavaFx yang perlu di beritahukan tentang perubahan yang
telah dibuat kepada daftar orang. Hal ini penting, jika tidak tampilan tidak akan sinkron dengan
data. Untuk tujuan ini, JavaFX mengenalkan beberapa Collection classes.
Dari koleksi-koleksi tersebut, kita memerlukan ObservableList. Untuk membuat
ObservableListbaru, tambah kan kode berikut pada awal kelas MainApp. Kita juga akan
menambah pembangun yang membuat beberapa contoh data dan metode pengambil umum
(public, getter).
MainApp.java
// ... AFTER THE OTHER VARIABLES ...
/**
* The data as an observable list of Persons.
*/
25
private ObservableList<Person> personData =
FXCollections.observableArrayList();
/**
* Constructor
*/
public MainApp() {
// Add some sample data
personData.add(new Person("Hans", "Muster"));
personData.add(new Person("Ruth", "Mueller"));
personData.add(new Person("Heinz", "Kurz"));
personData.add(new Person("Cornelia", "Meier"));
personData.add(new Person("Werner", "Meyer"));
personData.add(new Person("Lydia", "Kunz"));
personData.add(new Person("Anna", "Best"));
personData.add(new Person("Stefan", "Meier"));
personData.add(new Person("Martin", "Mueller"));
}
/**
* Returns the data as an observable list of Persons.
* @return
*/
public ObservableList<Person> getPersonData() {
return personData;
}
// ... THE REST OF THE CLASS ...
26
PersonOverviewController
Sekarang mari kita memberi data pada table. Kita perlu pengendali untuk
PersonOverview.fxml.
1. Buat kelas didalam paket view bernama PersonOverviewController.java. (Kita harus
menaruh dipaket yang sama PersonOverview.fxml,jika tidak Scene Builder tidak akan bisa
menemukannya).
2. Kita akan menambah beberapa variable instansi yang memberikan akses ke pada table dan
label didalam tampilan Bidang dan beberpa metode memiliki keterangan @FXML khusus. Hal
ini diperlukan oleh berkas fxml untuk dapat mengakses bidang pribadi dan metode pribadi.
Setelah kita mengatur semuanya di berkas fxml, aplikasi akan secara otomatis mengisi variabel
variabel ketika fxml telah dimuat. Jadi tambahkan kode berikut :
Kita hanya menggunakan nilai StringProperty untuk tabel kolom didalam contoh ini. Ketika
anda ingin menggunakan IntegerProperty atau DoubleProperty,
setCellValueFactory(...) harus mempunyai tambahan asObject():
myIntegerColumn.setCellValueFactory(cellData ->
cellData.getValue().myIntegerProperty().asObject());
Ini sangat diperlukan karena keputusan desain buruk dari JavaFX.
PersonOverviewController.java
package ch.makery.address.view;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import ch.makery.address.MainApp;
import ch.makery.address.model.Person;
public class PersonOverviewController {
@FXML
private TableView<Person> personTable;
27
@FXML
private TableColumn<Person, String> firstNameColumn;
@FXML
private TableColumn<Person, String> lastNameColumn;
@FXML
private Label firstNameLabel;
@FXML
private Label lastNameLabel;
@FXML
private Label streetLabel;
@FXML
private Label postalCodeLabel;
@FXML
private Label cityLabel;
@FXML
private Label birthdayLabel;
// Reference to the main application.
private MainApp mainApp;
/**
* The constructor.
* The constructor is called before the initialize() method.
*/
public PersonOverviewController() {
}
/**
* Initializes the controller class. This method is automatically called
28
* after the fxml file has been loaded.
*/
@FXML
private void initialize() {
// Initialize the person table with the two columns.
firstNameColumn.setCellValueFactory(cellData ->
cellData.getValue().firstNameProperty());
lastNameColumn.setCellValueFactory(cellData ->
cellData.getValue().lastNameProperty());
}
/**
* Is called by the main application to give a reference back to itself.
*
* @param mainApp
*/
public void setMainApp(MainApp mainApp) {
this.mainApp = mainApp;
// Add observable list data to the table
personTable.setItems(mainApp.getPersonData());
}
}
Penjelasan:
• Semua bidang dan metode dimana berkas fxml perlu akses, harus dikasi keterangan @FXML.
Sebenarnya hanya jika mereka bersifat pribadi (private), tetapi akan lebih baik jika mereka
bersifat pribadi (private) dan tanda mereka dengan keterangan.
• metode initialize() secara otomatis dipanggil setelah berkas fxml telah dimuat. Pada saat
ini bidang-bidang FXML seharusnya sudah terinisialisasi.
• metoda setCellValueFactory(...) yang telah kita atur pada kolom tabel digunakan
untuk menjelaskan bidang didalam objek Person yang digunakan sebagai kolom. Tanda panah
29
-> menunjukan bahwa kita menggunakan fitur Java yang dinamakan Lambdas*. (Pilihan lain
dengan menggunakan PropertyValueFactory, tetapi ini bukan tipe aman).
Menghubungkan MainApp dengan PersonOverviewController
Metode setMainApp(...) harus dipanggil oleh kelas MainApp. Ini memberi kita cara untuk
mengakses objek MainApp dan mendapat daftar dari Persons dan hal lainya. Ganti metode
showPersonOverview() dengan :
MainApp.java - new showPersonOverview() method
/**
* Shows the person overview inside the root layout.
*/
public void showPersonOverview() {
try {
// Load person overview.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/PersonOverview.fxml"));
AnchorPane personOverview = (AnchorPane) loader.load();
// Set person overview into the center of root layout.
rootLayout.setCenter(personOverview);
// Give the controller access to the main app.
PersonOverviewController controller = loader.getController();
controller.setMainApp(this);
} catch (IOException e) {
e.printStackTrace();
}
}
30
Kaitkan Tampilan Ke Pengendali
Kita hampir selesai, tapi satu hal terlewat, kita belum meberitahu PersonOverview.fxml
kontroller mana yang digunakan dan elemen mana yang cocok dengan bidang didalam
pengendali.
1. Buka PersonOverview.fxml dengan SceneBuilder.
2. Buka kelompok Controller pada sisi kiri dan pilih PersonOverviewController
sebagaicontroller class.
3. Pilih TableView di kelompok Hierarchy dan pilih Code, kelompokan bidang
personTable sebagai fx:id.
4. Lakukan hal yang sama untuk kolom-kolom dan pilih firstNameColumn dan
lastNameColumn sebagai masing-masing fx:id.
5. Untuk tiap label each label di kolom kedua, pilih fx:id yang sesuai.
31
6. Penting: Pada Eclipse segarkan (F5) keseluruhan proyek AddressApp. Ini
diperlukan karena Eclipse terkadang tidak mengetahuiu adanya perubahan yang dibuat
oleh Scene Builder.
Memulai Aplikasi
Ketika kamu memulai aplikasi saat ini, kamu seharusnya melihat tampilan tepat seperti hasil
tangkapan layar di awal artikel ini.
Berikutnya?
Di Tutorial bagian 3 kita akan menambah lebih banyak fungsi seperti penambahan, menghapus,
memperbaharui Persons.
32
Tutorial JavaFX 8 - Bagian 3: Interaksi
pengguna
Apr 24, 2014 • Updated Mar 12, 2015
Pembahasan dibagian 3
• Beraksi pada perubahan pemilihan pada table.
• Menambah fungsi tombol tambah, pembaharuan, dan hapus.
• Buat dialog munculan untuk memperbaharui person.
• Validasi masukan pengguna.
Beraksi Pada Pemilihan Table
33
Jelas kita belum menggunakan sisi tepat dari aplikasi. Ide untuk menampilkan detail mengengai
person disi kanan ketika pengguuna memilih person di table.
Pertama tambah metode didalam PersonOverviewController yang membantu kita untuk
mengisi label-label dengan data dari sebuah Person.
Buat metode bernama showPersonDetails(Person person). Buat semua label-label dengan
mengatur teks menggunakan setText(...) dengan detail dari person. Jika parameter null
dilewatkan, maka semua label harus bersih.
PersonOverviewController.java
/**
* Fills all text fields to show details about the person.
* If the specified person is null, all text fields are cleared.
*
* @param person the person or null
*/
private void showPersonDetails(Person person) {
if (person != null) {
// Fill the labels with info from the person object.
firstNameLabel.setText(person.getFirstName());
lastNameLabel.setText(person.getLastName());
streetLabel.setText(person.getStreet());
postalCodeLabel.setText(Integer.toString(person.getPostalCode()));
cityLabel.setText(person.getCity());
// TODO: We need a way to convert the birthday into a String!
// birthdayLabel.setText(...);
} else {
// Person is null, remove all the text.
firstNameLabel.setText("");
lastNameLabel.setText("");
streetLabel.setText("");
34
postalCodeLabel.setText("");
cityLabel.setText("");
birthdayLabel.setText("");
}
}
Mengganti Birthday Date ke String
Anda akan melihat bahwa kita tidak dapat mengatur birthday ke label Label dikarenakan
memiliki tipe LocalDate dan bukan String. Kita perlu membentuk tanggal lebih dahulu.
Kita akan menggunakan perubahan dari LocalDate dan String di beberapa tempat. Ini praktik
yang baik unutk menciptakan kelas pembantu dengan metode static. Kita akan memanggil
ini DateUtil dan menaruh dipaket terpisah bernama ch.makery.address.util:
DateUtil.java
package ch.makery.address.util;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
/**
* Helper functions for handling dates.
*
* @author Marco Jakob
*/
public class DateUtil {
/** The date pattern that is used for conversion. Change as you wish. */
private static final String DATE_PATTERN = "dd.MM.yyyy";
/** The date formatter. */
private static final DateTimeFormatter DATE_FORMATTER =
35
DateTimeFormatter.ofPattern(DATE_PATTERN);
/**
* Returns the given date as a well formatted String. The above defined
* {@link DateUtil#DATE_PATTERN} is used.
*
* @param date the date to be returned as a string
* @return formatted string
*/
public static String format(LocalDate date) {
if (date == null) {
return null;
}
return DATE_FORMATTER.format(date);
}
/**
* Converts a String in the format of the defined {@link
DateUtil#DATE_PATTERN}
* to a {@link LocalDate} object.
*
* Returns null if the String could not be converted.
*
* @param dateString the date as String
* @return the date object or null if it could not be converted
*/
public static LocalDate parse(String dateString) {
try {
return DATE_FORMATTER.parse(dateString, LocalDate::from);
} catch (DateTimeParseException e) {
return null;
36
}
}
/**
* Checks the String whether it is a valid date.
*
* @param dateString
* @return true if the String is a valid date
*/
public static boolean validDate(String dateString) {
// Try to parse the String.
return DateUtil.parse(dateString) != null;
}
}
Petunjuk: Kamu bisa merubah bentu dari tanggal dengan mengganti DATE_PATTERN. Untuk semua
kemungkinan pola ang ada lihat DateTimeFormatter.
Gunakan DateUtil
Sekarang kita perlu menggunakan DateUtil yang baru di metod showPersonDetails dari
PersonOverviewController. Ganti TODO yang telah ada sebelumnya dengan baris berikut:
birthdayLabel.setText(DateUtil.format(person.getBirthday()));
Mendengarkan Perubahan Pemilihan
Untuk mendapat informasi ketika pengguna memilih person di tabel person, kita perlu
mendengarkan perubahan-perubahan.
Ada antarmuka yang di JavaFX bernama ChangeListener dengan sebuah metode bernama
changed(...). Metode ini memiliki tiga parameter: observable, oldValue, and newValue.
Kita akan menciptakan sebuah ChangeListener menggunakan Java 8 lambda expression. Mari
tambahkan beberapa baris ke metode initialize() didalamPersonOverviewController.
Sekarang ini terlihat seperti ini:
37
PersonOverviewController.java
@FXML
private void initialize() {
// Initialize the person table with the two columns.
firstNameColumn.setCellValueFactory(
cellData -> cellData.getValue().firstNameProperty());
lastNameColumn.setCellValueFactory(
cellData -> cellData.getValue().lastNameProperty());
// Clear person details.
showPersonDetails(null);
// Listen for selection changes and show the person details when changed.
personTable.getSelectionModel().selectedItemProperty().addListener(
(observable, oldValue, newValue) ->
showPersonDetails(newValue));
}
Dengan showPersonDetails(null); kita mengatur ulang person detail.
Dengan personTable.getSelectionModel... kita mendapatkan selectedItemProperty dari
tabel person dan menambah pendengar. Kapan saja pengguna memilih person di tabel Ekspresi
lambda di eksekusi. Kita mengambil person baru terpilih dan melewatkannya ke metode
showPersonDetails(...).
Coba jalankan aplikasi anda, pastikan ketika anda memilih person di tabel, detail mengenai
person ditampilkan pada sisi kanan.
Jika tidak berfungsi, anda bisa membandingkan kelas PersonOverviewController punya
anda dengan PersonOverviewController.java.
38
Tombol Hapus
Antarmuka pengguna telah berisi tombol hapus, tapi tanpa fungsi. Kita kan membuat aksi untuk
tombol didalam Scene Builder. Metode apapun didalam kontroller kita yang berketerangan
@FXML (atau publik) adalah dapat diakses oleh Scene Builder. Jadi, mari pertama kita tambahkan
metode hapus pada akhir kelas PersonOverviewController:
PersonOverviewController.java
/**
* Called when the user clicks on the delete button.
*/
@FXML
private void handleDeletePerson() {
int selectedIndex = personTable.getSelectionModel().getSelectedIndex();
personTable.getItems().remove(selectedIndex);
}
Sekarang buka berkas PersonOverview.fxml dari SceneBuilder. Pilih tombol hapus, buka
kelompok Code dan pilih handleDeletePerson di dropdown of On Action.
Ingat: Setelah membuat perubahan pada berkas FXML di Scene Builder, mungkin perlu di segarkan (f5)
pada Eclipse, agar perubahan diterapkan.
Penanganan Kesalahan
Jika anda menjalankan Aplikasi, anda seharusnya dapat menghapus person terpilih dari tabel.
Tapi apa yang terjadi jika tombol delete di klik, tapi tiada person yang terpilih di tabel ?
Akan ada ArrayIndexOutOfBoundsException karena tidak dapat menghapus person pada
indeks -1. Indeks -1 dikembalikan oleh getSelectedIndex() - yang berarti tidak ada terpilih.
39
Tentu saja mengabaikan kesalahan seperti ini tidak bagus. Kita harus membiarkan pengguna
tahu bahwa dia harus memilih person sebelum melakukan penghapusan. (Bahkan lebih baik
jika kita melumpuhkan tombol hapus sehingga pengguna tidak dapat kesempatan untuk
melakukan hal yang salah).
Kita akan menambah dialog munculan untuk memberitahu pengguna. Anda akan perlu
menambah pustaka untuk Dialogs:
Dengan beberapa perubahan pada metode handleDeletePerson(), kita bisa menampilkan
dialog munculan sederhana ketika pengguna tekan tombol hapus saat tidak ada persoon terpilih
pada tabel:
PersonOverviewController.java
/**
* Called when the user clicks on the delete button.
*/
@FXML
private void handleDeletePerson() {
int selectedIndex = personTable.getSelectionModel().getSelectedIndex();
if (selectedIndex >= 0) {
personTable.getItems().remove(selectedIndex);
} else {
// Nothing selected.
Dialogs.create()
Alert alert = new Alert(AlertType.WARNING);
alert.initOwner(mainApp.getPrimaryStage());
alert.setTitle("No Selection");
alert.setHeaderText("No Person Selected");
alert.setContentText("Please select a person in the table.");
alert.showAndWait();
}
}
40
Dialog Baru dan Pembaharuan
Aksi baru dan pembaharuan perlu sedikit tindakan: Kita akan perlu kustom dialog (stage baru)
dengan bentuk meminta pengguna untuk detail tentang person.
Desain Dialog
1. Buat fxml baru bernama PersonEditDialog.fxml didalam paket view.
2. Gunakan GridPane, Label, TextField dan Button untuk membuat dalog seperti:
Jika anda tidak ingin repot, bisa mengunduh pada PersonEditDialog.fxml.
41
Buat Pengendali
Buat Pengendali untuk Dialog sebagai PersonEditDialogController.java:
PersonEditDialogController.java
package ch.makery.address.view;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import org.controlsfx.dialog.Dialogs;
import ch.makery.address.model.Person;
import ch.makery.address.util.DateUtil;
/**
* Dialog to edit details of a person.
*
* @author Marco Jakob
*/
public class PersonEditDialogController {
@FXML
private TextField firstNameField;
@FXML
private TextField lastNameField;
@FXML
private TextField streetField;
@FXML
42
private TextField postalCodeField;
@FXML
private TextField cityField;
@FXML
private TextField birthdayField;
private Stage dialogStage;
private Person person;
private boolean okClicked = false;
/**
* Initializes the controller class. This method is automatically called
* after the fxml file has been loaded.
*/
@FXML
private void initialize() {
}
/**
* Sets the stage of this dialog.
*
* @param dialogStage
*/
public void setDialogStage(Stage dialogStage) {
this.dialogStage = dialogStage;
}
/**
* Sets the person to be edited in the dialog.
43
*
* @param person
*/
public void setPerson(Person person) {
this.person = person;
firstNameField.setText(person.getFirstName());
lastNameField.setText(person.getLastName());
streetField.setText(person.getStreet());
postalCodeField.setText(Integer.toString(person.getPostalCode()));
cityField.setText(person.getCity());
birthdayField.setText(DateUtil.format(person.getBirthday()));
birthdayField.setPromptText("dd.mm.yyyy");
}
/**
* Returns true if the user clicked OK, false otherwise.
*
* @return
*/
public boolean isOkClicked() {
return okClicked;
}
/**
* Called when the user clicks ok.
*/
@FXML
private void handleOk() {
if (isInputValid()) {
44
person.setFirstName(firstNameField.getText());
person.setLastName(lastNameField.getText());
person.setStreet(streetField.getText());
person.setPostalCode(Integer.parseInt(postalCodeField.getText()));
person.setCity(cityField.getText());
person.setBirthday(DateUtil.parse(birthdayField.getText()));
okClicked = true;
dialogStage.close();
}
}
/**
* Called when the user clicks cancel.
*/
@FXML
private void handleCancel() {
dialogStage.close();
}
/**
* Validates the user input in the text fields.
*
* @return true if the input is valid
*/
private boolean isInputValid() {
String errorMessage = "";
if (firstNameField.getText() == null ||
firstNameField.getText().length() == 0) {
45
errorMessage += "No valid first name!\n";
}
if (lastNameField.getText() == null ||
lastNameField.getText().length() == 0) {
errorMessage += "No valid last name!\n";
}
if (streetField.getText() == null || streetField.getText().length() == 0) {
errorMessage += "No valid street!\n";
}
if (postalCodeField.getText() == null ||
postalCodeField.getText().length() == 0) {
errorMessage += "No valid postal code!\n";
} else {
// try to parse the postal code into an int.
try {
Integer.parseInt(postalCodeField.getText());
} catch (NumberFormatException e) {
errorMessage += "No valid postal code (must be an
integer)!\n";
}
}
if (cityField.getText() == null || cityField.getText().length() ==
0) {
errorMessage += "No valid city!\n";
}
if (birthdayField.getText() == null ||
birthdayField.getText().length() == 0) {
errorMessage += "No valid birthday!\n";
} else {
46
if (!DateUtil.validDate(birthdayField.getText())) {
errorMessage += "No valid birthday. Use the format
dd.mm.yyyy!\n";
}
}
if (errorMessage.length() == 0) {
return true;
} else {
// Show the error message.
Dialogs.create()
Alert alert = new Alert(AlertType.ERROR);
alert.initOwner(dialogStage);
alert.setTitle("Invalid Fields");
alert.setHeaderText("Please correct invalid fields");
alert.setContentText(errorMessage);
alert.showAndWait();
return false;
}
}
}
Beberapa hal mengenai pengendali ini:
• Metode setPerson(...) dapat dipanggil dari kelas lain untuk mengatur person yang di
perbaharui.
• Ketika pengguna menekan tombol OK, metode handleOk() dipanggil. Pertama beberapa
validasi dilakukan dengan memanggil metode isInputValid(). Hanya jika validasi telah
berhasil, objek person di isi dengan data yang telah dimasukan oleh pengguna. Perubahan ini
akan secara langsung di terapkan ke objek person yang telah di lewatkan ke
setPerson(...)!
47
• boolean okClicked digunakan sehinga pemanggil dapat menentukan ketika pengguna
menekan tombol Ok atau Cancel.
Kaitkan Tampilan dan Pengendali
Dengan dibuatnya tampilan (fxml) dan pengendali, kita perlu menghubungkan mereka
bersama:
1. Buka PersonEditDialog.fxml.
2. Di kelompok Controller dibagian kiri, pilih PersonEditDialogController sebagai kelas
controller class.
3. Atur fx:id pada semua TextField dengan pengendali yang sesuai.
4. Atur onAction dari dua tombol ke metode penanganan yang sesuai.
Membuka Dialog
Tambah metode untuk memuat dan menampilkan person dialog didalam MainApp:
MainApp.java
/**
* Opens a dialog to edit details for the specified person. If the user
* clicks OK, the changes are saved into the provided person object and true
* is returned.
*
* @param person the person object to be edited
* @return true if the user clicked OK, false otherwise.
*/
public boolean showPersonEditDialog(Person person) {
try {
// Load the fxml file and create a new stage for the popup dialog.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/PersonEditDialog.fxml"))
;
AnchorPane page = (AnchorPane) loader.load();
48
// Create the dialog Stage.
Stage dialogStage = new Stage();
dialogStage.setTitle("Edit Person");
dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.initOwner(primaryStage);
Scene scene = new Scene(page);
dialogStage.setScene(scene);
// Set the person into the controller.
PersonEditDialogController controller = loader.getController();
controller.setDialogStage(dialogStage);
controller.setPerson(person);
// Show the dialog and wait until the user closes it
dialogStage.showAndWait();
return controller.isOkClicked();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
Tambahkan metode berikut pada PersonOverviewController. Metode-metode ini akan
memanggil showPersonEditDialog(...) dariMainApp ketika pengguna menekan tombol
baru atau pembaharuan.
PersonOverviewController.java
/**
* Called when the user clicks the new button. Opens a dialog to edit
* details for a new person.
*/
49
@FXML
private void handleNewPerson() {
Person tempPerson = new Person();
boolean okClicked = mainApp.showPersonEditDialog(tempPerson);
if (okClicked) {
mainApp.getPersonData().add(tempPerson);
}
}
/**
* Called when the user clicks the edit button. Opens a dialog to edit
* details for the selected person.
*/
@FXML
private void handleEditPerson() {
Person selectedPerson =
personTable.getSelectionModel().getSelectedItem();
if (selectedPerson != null) {
boolean okClicked = mainApp.showPersonEditDialog(selectedPerson);
if (okClicked) {
showPersonDetails(selectedPerson);
}
} else {
// Nothing selected.
Dialogs.create()
Alert alert = new Alert(AlertType.WARNING);
alert.initOwner(mainApp.getPrimaryStage());
alert.setTitle("No Selection");
alert.setHeaderText("No Person Selected");
alert.setContentText("Please select a person in the table.");
50
alert.showAndWait();
}
}
Buka berkas PersonOverview.fxml di Scene Builder. Pilih metode yang sesuai di On Action
ntuk tombol baru dan pembaharuan.
Selesai!
Anda seharusnya memiliki Address Application yang telah berfungsi. Aplikasi ini telah dapat
menambah, pembaharuan, dan menghapus persons. Bahkan ada beberapa pengesahan untuk
bidang teks untuk menghindari kesalahan masukan dari pengguna.
Saya harap konsep dan struktur dari aplikasi ini akan dapat membuat anda menulis Aplikasi
JavaFX anda sendiri.
Berikutnya?
Tutorial Bagian 4 kita akan menambahkan gaya CSS.
51
Tutorial JavaFX 8 - Bagian 4: Memberikan
gaya dengan CSS
Apr 25, 2014 • Updated Jan 05, 2015
Pembahasan di bagian 4
• Memberi gaya CSS
• Menambah Ikon Aplikasi
Memberi gaya CSS
Di JavaFX anda bisa memberi gaya pada antarmuka pengguna menggunakan Cascading Style
Sheet (CSS). Ini adalah hal yang sangat hebat, tidak pernah semudah ini untuk mengubah
sesuaikan tampilan JavaFX.
Di tutorial ini kita akan membuat DarkTheme terinspirasi oleh desain Windows 8 Metro. Css
untuk tombol berdasarkan JMetro - Windows 8 Metro controls on Java oleh Pedro Duque
Vieira.
52
Membuat Terbiasa dengan CSS
Jika anda ingin memberi gaya pada aplikasi JavaFX anda seharusnya memiliki pengetahuan
dasar mengenai CSS secara umum. Tempat yang bagus untuk memulainya adalah CSS tutorial.
Untuk lebih banyak informasi terkait CSS di JavaFX :
• Skinning JavaFX Applications with CSS - Tutorial oleh Oracle
• JavaFX CSS Reference - Referensi Resmi
JavaFX CSS Baku
Sumber CSS baku dalam JavaFX 8 di panggil modena.css. Berkas css ini dapat di temukan
pada berkas jfxrt.jar bertempat di folder java yang berlokasi di
/jdk1.8.x/jre/lib/ext/jfxrt.jar.
Unzip the jfxrt.jar. Anda akan menemukan modena.css didalam
com/sun/javafx/scene/control/skin/modena/
Gaya baku ini selalu diterapkan ke aplikasi JavaFX. Dengan menambah CSS yang disesuaikan
kita dapat mengesampingkan gaya baku dari modena.css.
Petunjuk: Akan membantu untuk melihat CSS baku, untuk melihat gaya mana yang perlu untuk
dikesampingkan
Melampirkan CSS
Tambahkan CSS berikut yang bernama DarkTheme.css kedalam paket view.
DarkTheme.css
.background {
-fx-background-color: #1d1d1d;
}
.label {
-fx-font-size: 11pt;
-fx-font-family: "Segoe UI Semibold";
-fx-text-fill: white;
-fx-opacity: 0.6;
}
53
.label-bright {
-fx-font-size: 11pt;
-fx-font-family: "Segoe UI Semibold";
-fx-text-fill: white;
-fx-opacity: 1;
}
.label-header {
-fx-font-size: 32pt;
-fx-font-family: "Segoe UI Light";
-fx-text-fill: white;
-fx-opacity: 1;
}
.table-view {
-fx-base: #1d1d1d;
-fx-control-inner-background: #1d1d1d;
-fx-background-color: #1d1d1d;
-fx-table-cell-border-color: transparent;
-fx-table-header-border-color: transparent;
-fx-padding: 5;
}
.table-view .column-header-background {
-fx-background-color: transparent;
}
.table-view .column-header, .table-view .filler {
-fx-size: 35;
54
-fx-border-width: 0 0 1 0;
-fx-background-color: transparent;
-fx-border-color:
transparent
transparent
derive(-fx-base, 80%)
transparent;
-fx-border-insets: 0 10 1 0;
}
.table-view .column-header .label {
-fx-font-size: 20pt;
-fx-font-family: "Segoe UI Light";
-fx-text-fill: white;
-fx-alignment: center-left;
-fx-opacity: 1;
}
.table-view:focused .table-row-cell:filled:focused:selected {
-fx-background-color: -fx-focus-color;
}
.split-pane:horizontal > .split-pane-divider {
-fx-border-color: transparent #1d1d1d transparent #1d1d1d;
-fx-background-color: transparent, derive(#1d1d1d,20%);
}
.split-pane {
-fx-padding: 1 0 0 0;
}
55
.menu-bar {
-fx-background-color: derive(#1d1d1d,20%);
}
.context-menu {
-fx-background-color: derive(#1d1d1d,50%);
}
.menu-bar .label {
-fx-font-size: 14pt;
-fx-font-family: "Segoe UI Light";
-fx-text-fill: white;
-fx-opacity: 0.9;
}
.menu .left-container {
-fx-background-color: black;
}
.text-field {
-fx-font-size: 12pt;
-fx-font-family: "Segoe UI Semibold";
}
/*
* Metro style Push Button
* Author: Pedro Duque Vieira
* http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-
java/
*/
56
.button {
-fx-padding: 5 22 5 22;
-fx-border-color: #e2e2e2;
-fx-border-width: 2;
-fx-background-radius: 0;
-fx-background-color: #1d1d1d;
-fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
-fx-font-size: 11pt;
-fx-text-fill: #d8d8d8;
-fx-background-insets: 0 0 0 0, 0, 1, 2;
}
.button:hover {
-fx-background-color: #3a3a3a;
}
.button:pressed, .button:default:hover:pressed {
-fx-background-color: white;
-fx-text-fill: #1d1d1d;
}
.button:focused {
-fx-border-color: white, white;
-fx-border-width: 1, 1;
-fx-border-style: solid, segments(1, 1);
-fx-border-radius: 0, 0;
-fx-border-insets: 1 1 1 1, 0;
}
.button:disabled, .button:default:disabled {
57
-fx-opacity: 0.4;
-fx-background-color: #1d1d1d;
-fx-text-fill: white;
}
.button:default {
-fx-background-color: -fx-focus-color;
-fx-text-fill: #ffffff;
}
.button:default:hover {
-fx-background-color: derive(-fx-focus-color,30%);
}
Kita sekarang perlu melampirkan CSS ke Scene. Kita dapat melakukan ini melalui kode, tetapi
kita akan menggunakan Scene Builder untuk menambah ke berkas fxml:
Melampirkan CSS ke RootLayout.fxml
1. Buka berkas RootLayout.fxml di Scene Builder.
2. Pilih akar BorderPane di Hierarchy. Dibawah kelompok Properties tambah berkas
DarkTheme.css file as stylesheet.
58
Lampirkan CSS ke PersonEditDialog.fxml
1. Buka berkas PersonEditDialog.fxml di Scene Builder. Pilih akar AnchorPane dan
pilih DarkTheme.css in the kelompok Properties sebagai stylesheet.
2. atar belakang masih berwarna putih, jadi tambah kelas gaya background ke akar
AnchorPane.
3. Pilih tombol OK dan pilih Default Button di tampilan Properties. Ini akan merubah
warnah dan membuat tombol baku ketika tombol enter ditekan oleh pengguna.
Lampirkan CSS ke PersonOverview.fxml
1. Buka berkas PersonOverview.fxml di Scene Builder. Pilih akar AnchorPane pada
kelompok Hierarchy. Pada Properties tambahkan DarkTheme.css sebagai stylesheet.
2. Anda seharusnay melihat beberapa perubahan saat ini. Tabel dan tombol berwarna
hitam. Semua kelas gaya .table-view dan .button dari modena.css diterapkan ke
tabel dan tombol. Karena kita telah mendefinisikan ulang (dan dengan demikian diganti)
beberapa dari gaya di penyesuaian CSS kita, gaya baru akan diterapkan secara otomatis.
3. Anda mungkin perlu menyesuaian ukuran dari tombol sehingga semua teks ditampilkan.
4. Pilih AnchorPane yang tepat didalam SplitPane.
59
5. Pada keompok Properties pilih background sebagai kelas gaya. Latarbelakang akan
berwarna hitam.
Label dengan Gaya yang berbeda
Saat ini semua label pada sisi kanan memliki ukuran yang sama. Sudah ada beberapa gaya yang
didefinisikan dalam berkas CSS bernama.label-header dan .label-bright kita akan
gunakan ini sebagai gaya.
1. Pilih label Person Details dan tambahkan label-header sebagai kelas gaya.
2. Untuk tiap label di sisi kanan kolom (dimana rinncian person ditampilkan) tambah kelas
gaya label-bright.
60
Menambah Ikon Aplikasi
Saat ini aplikasi kita hanya memiliki ikon baku di palang judul dan palang tugas:
Akan lebih terlihat lebih baik jika memiliki ikon khusus:
Berkas Ikon
Tempat yang memungkinkan untuk mendapatkan ikon2 ada di Icon Finder. Saya telah
mengunduhe address book icon.
Buat sebuah folder didalam proyek AddressApp bernama resources dan subfolder bernama
images.Taruh ikon dalam images folder. Struktur folder anda akan terlihat seperti :
61
Atur Ikon Ke Scene
Untuk mengatur ikon pada scene, tambahkan baris berikut pada metode start(...) didalam
MainApp.java
MainApp.java
this.primaryStage.getIcons().add(new
Image("file:resources/images/address_book_32.png"));
Keseluruhan metode start(...) terlihat seperti ini :
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("AddressApp");
// Set the application icon.
this.primaryStage.getIcons().add(new
Image("file:resources/images/address_book_32.png"));
initRootLayout();
showPersonOverview();
}
Anda tentunya juga dapat menambah ikon pada stage dari dialog pembaharuan person:
Berikutnya?
Didalam Tutorial Bagian 5 kita akan menggunakan XML sebagai penyimpanan data.
62
Tutorial JavaFX 8 - Bagian 5: Menyimpan
data sebagai XML
Apr 25, 2014 • Updated Jan 05, 2015
Pembahasan di bagian 5
• Memaksa data sebagai XML
• Menggunakan JavaFX FileChooser
• Menggunakan JavaFX Menu
• Menyimpan berkas terbuka paling akhir di preferensi pengguna
Hingga saat ini aplikasi alamat kita hanya menyimpan data di memori. Setiap saat kita menutup
aplikasi, data akan hilang. Jadi udah waktunya kita mulai berpikir menyimpan data.
63
Menyimpan Preferensi Pengguna
Java mengijinkan kta untuk menyimpan keadaan aplikasi menggunakan kelas bernama
Preferences. Bergantung pada sistem operasi, Preferences disimpan di beberapa tempat(
berkas pendaftaran di Window).
Kita tidak akan bisa menyimpan Preferences untuk menyimpan keseluruhan data alamat.
Tetapi ini mengijinkan kita untuk menyimpan keadaan aplikasi. Salah satunya adalah alamat
pada berkas yang terakhir kali dibuka. Dengan informasi ini, kita dapat memuat keadaan
terakhir aplikasi ketika pengguna menjalankan kembali aplikasi.
Dua metode ini menangani penyimpanan dan menerima preferensi. Tambah kode ini ke akhir
kelas MainApp:
MainApp.java
/**
* Returns the person file preference, i.e. the file that was last opened.
* The preference is read from the OS specific registry. If no such
* preference can be found, null is returned.
*
* @return
*/
public File getPersonFilePath() {
Preferences prefs = Preferences.userNodeForPackage(MainApp.class);
String filePath = prefs.get("filePath", null);
if (filePath != null) {
return new File(filePath);
} else {
return null;
}
}
/**
* Sets the file path of the currently loaded file. The path is persisted in
64
* the OS specific registry.
*
* @param file the file or null to remove the path
*/
public void setPersonFilePath(File file) {
Preferences prefs = Preferences.userNodeForPackage(MainApp.class);
if (file != null) {
prefs.put("filePath", file.getPath());
// Update the stage title.
primaryStage.setTitle("AddressApp - " + file.getName());
} else {
prefs.remove("filePath");
// Update the stage title.
primaryStage.setTitle("AddressApp");
}
}
Memaksa Data sebagai XML
Kenapa XML?
Salah satu cara paling umum untuk memaksa data menggunakan basis data. Nasis data biasanya
berisi beberapa jenis hubungan data (seperti tabel) selama data yang kita perlukan menyimpan
objek. Ini di sebut object-relational ketidak cocokan impedansi (object-relational impedance
mismatch).Ini sedikit perlu usaha untuk mencocokan objek ke tabel database relasional. Ada
beberapa kerangka kerja yang dapat membantu mencocokan (contoh Hibernate, the most
popular one) tetapi ini tetap memerlukan beberapa usaha.
Untuk model data sederhana kita, akan sangat mudah menggunakan XML. Kita akan
menggunakan pustaka bernama JAXB (Java Architecture for XML Binding). Dengan beberapa
baris kode JAXB akan memungkinkan kita membangkitkan keluaran XML seperti:
65
Example xml output
<persons>
<person>
<birthday>1999-02-21</birthday>
<city>some city</city>
<firstName>Hans</firstName>
<lastName>Muster</lastName>
<postalCode>1234</postalCode>
<street>some street</street>
</person>
<person>
<birthday>1999-02-21</birthday>
<city>some city</city>
<firstName>Anna</firstName>
<lastName>Best</lastName>
<postalCode>1234</postalCode>
<street>some street</street>
</person>
</persons>
Menggunakan JAXB
JAXB telah termasuk dalam JDK. Ini berarti kita tidak perlu menyertakan pustaka tambahan.
JAXB menyedikan 2 fitur utama: Kemampuan untuk mengumpulkan java objek ke XML, dan
memecah data XML ke java objek.
Agar JAXB dapat melakukan perubahan, kita perlu mempersiapkan model.
Mempersiapkan Kelas Model Untuk JAXB
Data yang mau disimpan menetap di variabel personData didalam kelas MainApp. AXB
memerlukan kelas paling atas untuk di kasi keterangan @XmlRootElement. personData adalah
kelas ObservableList dan kita tidak bisa memberikan keterangan ke ObservableList. Jadi
kita perlu untuk membuat kelas lain yang hanyak akan digunakan untuk menampung daftar dari
Persons untuk menyimpan ke XML.
66
Kelas baru yang kita buat akan bernama PersonListWrapper dan taruh ke paket
ch.makery.address.model.
PersonListWrapper.java
package ch.makery.address.model;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Helper class to wrap a list of persons. This is used for saving the
* list of persons to XML.
*
* @author Marco Jakob
*/
@XmlRootElement(name = "persons")
public class PersonListWrapper {
private List<Person> persons;
@XmlElement(name = "person")
public List<Person> getPersons() {
return persons;
}
public void setPersons(List<Person> persons) {
this.persons = persons;
}
}
67
Perhatikan dua penejelasan :
• @XmlRootElement menjelaskan nama dari elemen akar.
• @XmlElement adalah pilihan nama yang bisa kita tentukan untuk elemen.
Membaca dan Menulis Data dengan JAXB
Kita akan membuat kelas MainApp bertanggungjawab untuk membaca dan menulis data Person.
Tambah dua metode barikut ke akhir MainApp.java:
/**
* Loads person data from the specified file. The current person data will
* be replaced.
*
* @param file
*/
public void loadPersonDataFromFile(File file) {
try {
JAXBContext context = JAXBContext
.newInstance(PersonListWrapper.class);
Unmarshaller um = context.createUnmarshaller();
// Reading XML from the file and unmarshalling.
PersonListWrapper wrapper = (PersonListWrapper) um.unmarshal(file);
personData.clear();
personData.addAll(wrapper.getPersons());
// Save the file path to the registry.
setPersonFilePath(file);
} catch (Exception e) { // catches ANY exception
Alert alert = new Alert(AlertType.ERROR);
alert.setTitle("Error");
68
alert.setHeaderText("Could not load data");
alert.setContentText("Could not load data from file:\n" +
file.getPath());
alert.showAndWait();
}
}
/**
* Saves the current person data to the specified file.
*
* @param file
*/
public void savePersonDataToFile(File file) {
try {
JAXBContext context = JAXBContext
.newInstance(PersonListWrapper.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// Wrapping our person data.
PersonListWrapper wrapper = new PersonListWrapper();
wrapper.setPersons(personData);
// Marshalling and saving XML to the file.
m.marshal(wrapper, file);
// Save the file path to the registry.
setPersonFilePath(file);
} catch (Exception e) { // catches ANY exception
Alert alert = new Alert(AlertType.ERROR);
69
alert.setTitle("Error");
alert.setHeaderText("Could not save data");
alert.setContentText("Could not save data to file:\n" +
file.getPath());
alert.showAndWait();
}
}
Pengumpulan/pemecahan telah siap, mari kita buat menu simpan/muat yang dapat digunakan.
Menangani Menu Aksi
Di RootLayout.fxml telah ada menu, tetap ita belum menggunakanya. Sebelum kita
menambah aksi ke menu, kita terlebih dahulu membuat bulir-bulir menu.
Buka berkas RootLayout.fxml di Scene Builder dan seret bulir menu yang diperlukan dari
kelompok library ke MenuBar di kelompok hierarchy. Buat New, Open..., Save, Save As...,
dan Exit bulir menu.
Petunjuk: Gunakan pengaturan Accelerator di kelompok Properties anda bisa membuat jalan
pintas pada menu.
RootLayoutController
Untuk menangani aksi menu, kita perlu menambah kelas pengendali. Buat sebuah kelas
RootLayoutController didalam paket ch.makery.address.view.
70
Tambahkan kode berikut ke controller:
RootLayoutController.java
package ch.makery.address.view;
import java.io.File;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.stage.FileChooser;
import org.controlsfx.dialog.Dialogs;
import ch.makery.address.MainApp;
/**
* The controller for the root layout. The root layout provides the basic
* application layout containing a menu bar and space where other JavaFX
* elements can be placed.
*
* @author Marco Jakob
*/
public class RootLayoutController {
// Reference to the main application
private MainApp mainApp;
/**
* Is called by the main application to give a reference back to itself.
*
71
* @param mainApp
*/
public void setMainApp(MainApp mainApp) {
this.mainApp = mainApp;
}
/**
* Creates an empty address book.
*/
@FXML
private void handleNew() {
mainApp.getPersonData().clear();
mainApp.setPersonFilePath(null);
}
/**
* Opens a FileChooser to let the user select an address book to load.
*/
@FXML
private void handleOpen() {
FileChooser fileChooser = new FileChooser();
// Set extension filter
FileChooser.ExtensionFilter extFilter = new
FileChooser.ExtensionFilter(
"XML files (*.xml)", "*.xml");
fileChooser.getExtensionFilters().add(extFilter);
// Show save file dialog
File file = fileChooser.showOpenDialog(mainApp.getPrimaryStage());
72
if (file != null) {
mainApp.loadPersonDataFromFile(file);
}
}
/**
* Saves the file to the person file that is currently open. If there is
no
* open file, the "save as" dialog is shown.
*/
@FXML
private void handleSave() {
File personFile = mainApp.getPersonFilePath();
if (personFile != null) {
mainApp.savePersonDataToFile(personFile);
} else {
handleSaveAs();
}
}
/**
* Opens a FileChooser to let the user select a file to save to.
*/
@FXML
private void handleSaveAs() {
FileChooser fileChooser = new FileChooser();
// Set extension filter
FileChooser.ExtensionFilter extFilter = new
FileChooser.ExtensionFilter(
"XML files (*.xml)", "*.xml");
73
fileChooser.getExtensionFilters().add(extFilter);
// Show save file dialog
File file = fileChooser.showSaveDialog(mainApp.getPrimaryStage());
if (file != null) {
// Make sure it has the correct extension
if (!file.getPath().endsWith(".xml")) {
file = new File(file.getPath() + ".xml");
}
mainApp.savePersonDataToFile(file);
}
}
/**
* Opens an about dialog.
*/
@FXML
private void handleAbout() {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("AddressApp");
alert.setHeaderText("About");
alert.setContentText("Author: Marco Jakob\nWebsite:
http://code.makery.ch");
alert.showAndWait();
}
/**
* Closes the application.
*/
@FXML
74
private void handleExit() {
System.exit(0);
}
}
FileChooser
Catat bahwa metode yang menggunakan kelas FileChooser didalam RootLayoutController.
ertama, sebuah kelas objek FileChooser dibuat. kemudian ekstensi saring di tambah sehingga
hanya berkas yang berakhiran .xml ditampilkan, dan terakhir fileChooser ditampilkan diatas
stage utama
Jika pengguna menutup dialog tanpa memilih sebuah berkas, nilai null kan dikembalikan. Jika
tidak, kita akan bisa memilih berkas dan melewatkanya ke loadPersonDataFromFile(...)
atau metode savePersonDataToFile(...) dari MainApp.
Menghubungkan tampilan fxml ke pengendali
1. Buka RootLayout.fxml di Scene Builder. Dikelompok Controller pilih
RootLayoutController sebagai Controller class.
2. Pada kelompok Hierarchy dan pilih sebuah bulir menu. Di Code dibawah On Action
anda seharusnya melihat pilihan dari semua metode pengendali yang ada. Pilih metode
yang tepat untuk tiap menu.
3. Ulangi langkah-langkah ini untuk tiap bulir menu.
75
4. Tutup Scenee Builder dan segarkan (F5) Ini akan membuat Eclipse tau telah ada
perubahan yang dilakukan dari Scene Builder.
Menghubungkan MainApp dan RootLayoutController
Di beberapa tempat, RootLayoutController perlu rujukan balik ke MainApp. Kita belum
memberikan rujukan ke RootLayoutController.
Buka kelas MainApp dan ganti metode initRootLayout() dengan:
/**
* Initializes the root layout and tries to load the last opened
* person file.
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class
.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
// Give the controller access to the main app.
RootLayoutController controller = loader.getController();
controller.setMainApp(this);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
76
// Try to load last opened person file.
File file = getPersonFilePath();
if (file != null) {
loadPersonDataFromFile(file);
}
}
Perhatikan ada dua perubahan: Baris-baris yang memberikan akses pengendali ke MainApp an
3 baris terakhir untuk memuat berkas person terakhir yang dibuka.
Percobaan
Melakukan ujicoba dari aplikasi anda seharusnya dapat menggunakan menu untuk menyimpan
data person ke sebuah berkas.
Ketika anda buka berkas xml di sebuat editor, anda akan mengetahui birthday tidak tersimpan
secara benar, tag <birthday/> kosong, dikarenakan JAXB tidak tahu bagaimana merubah
LocalDate ke XML. Kita harus menyediakan LocalDateAdapter yang telah disesuaikan
untuk menjelaskan perubahan ini.
Buat kelas baru didalam ch.makery.address.util bernama LocalDateAdapter berisi:
LocalDateAdapter.java
package ch.makery.address.util;
import java.time.LocalDate;
import javax.xml.bind.annotation.adapters.XmlAdapter;
/**
* Adapter (for JAXB) to convert between the LocalDate and the ISO 8601
* String representation of the date such as '2012-12-03'.
*
* @author Marco Jakob
*/
77
public class LocalDateAdapter extends XmlAdapter<String, LocalDate> {
@Override
public LocalDate unmarshal(String v) throws Exception {
return LocalDate.parse(v);
}
@Override
public String marshal(LocalDate v) throws Exception {
return v.toString();
}
}
Buka Person.java dan tambah keterangan pada metode getBirthday():
@XmlJavaTypeAdapter(LocalDateAdapter.class)
public LocalDate getBirthday() {
return birthday.get();
}
Sekarang coba lagi, Coba simpan dan muat berkas xml. Setelah memulai ulang, ini seharusnya
secara otomatis memuat berkas terakhir digunakan.
Bagaimana Ini Bekerja
Mari kita lihat bagaimana semua ini berfungsi:
1. Aplikasi telah dijalankan menggunakan metode main(...) didalam MainApp.
2. Pembangun public MainApp() dipanggil dan menambah beberapa contoh data.
3. Metode MainApps start(...) dipanggil dan memanggil initRootLayout() untuk
menginisialisasi tata letak dasar dari RootLayout.fxml. Berkas fxml memiliki informasi
mengenai pengendali mana yang digunakan dan hubungan tampilan pada
RootLayoutController.
4. MainApp mendapat RootLayoutController dari pemuat fxml, dan melewatkan sebuah
referensi ke pengendali itu sendiri. Dengan referensi ni, pengendali dapat mengakses publik
metode dari MainApp.
78
5. Pada akhir metode initRootLayout()kita akan mencoba untuk mendapatkan
berkasperson yang terakhir dibuka dari Preferences Jika Preferences telah tau berkas XML,
kita akan memuat data dari berkas XML ini. Ini tampaknya akan menulis ulang data contoh dari
pembangun.
Berikutnya
Pada Tutorial Bagian 6 kita akan menambah bagan statistika tanggal lahir.
79
Tutorial JavaFX 8 - Bagian 6: Bagan
Statistika
May 09, 2014 • Updated Mar 12, 2015
Pembahasan di bagian 6
• Membuat Bagan Statistika untuk menampilkan tanggal lahir.
Statistika Tanggal lahir
Semua orang di AddressApp memiliki tanggal lahir. Bukankan akan lebih menyenangkan jika
ada statistika tentang ketika mereka merayakannya.
Kita akan menggunakan Bagan Palang berisi sebuah palang untuk tiap bulan. Tiap palang
menampilkan berapa banyak orang yang memiliki tanggal lahir di bulan tersebut.
80
Tampilan FXML Statistik
1. Kita mulai dengan membuat berkas BirthdayStatistics.fxml didalam paket
ch.makery.address.view (Klik kanan pada paket | New | other... | New FXML
Document).
2. Buka berkas BirthdayStatistics.fxml di Scene Builder.
3. Pilih akar AnchorPane. Pada kelompok Layout atur Pref Width ke 620 dan Pref Height
ke 450.
4. Tambah BarChart ke AnchorPane.
5. Klik kanan padaBarChart dan pilih Fit to Parent.
6. Simpan berkas fxml, pada Eclipse segarkan (f5).
Sebelum kita beralih ke Scene Builder, kita akan terlebih dahulu membuat pengendali dan
menghubungkan semuanya pada MainApp.
Pengendali Statistika
DI paket tampilan ch.makery.address.view buat kelas java bernama
BirthdayStatisticsController.java.
Mari kita lihat pada keseluruhan kelas pengendali sebelum saya mulai menjelaskan:
81
BirthdayStatisticsController.java
package ch.makery.address.view;
import java.text.DateFormatSymbols;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.XYChart;
import ch.makery.address.model.Person;
/**
* The controller for the birthday statistics view.
*
* @author Marco Jakob
*/
public class BirthdayStatisticsController {
@FXML
private BarChart<String, Integer> barChart;
@FXML
private CategoryAxis xAxis;
private ObservableList<String> monthNames =
FXCollections.observableArrayList();
82
/**
* Initializes the controller class. This method is automatically called
* after the fxml file has been loaded.
*/
@FXML
private void initialize() {
// Get an array with the English month names.
String[] months =
DateFormatSymbols.getInstance(Locale.ENGLISH).getMonths();
// Convert it to a list and add it to our ObservableList of months.
monthNames.addAll(Arrays.asList(months));
// Assign the month names as categories for the horizontal axis.
xAxis.setCategories(monthNames);
}
/**
* Sets the persons to show the statistics for.
*
* @param persons
*/
public void setPersonData(List<Person> persons) {
// Count the number of people having their birthday in a specific
month.
int[] monthCounter = new int[12];
for (Person p : persons) {
int month = p.getBirthday().getMonthValue() - 1;
monthCounter[month]++;
}
83
XYChart.Series<String, Integer> series = new XYChart.Series<>();
// Create a XYChart.Data object for each month. Add it to the series.
for (int i = 0; i < monthCounter.length; i++) {
series.getData().add(new XYChart.Data<>(monthNames.get(i),
monthCounter[i]));
}
barChart.getData().add(series);
}
}
Bagaimana Pengendali Bekerja
1. Pengendali akan memerlukan akses ke 2 elemn dari berkas FXML :
o barChar: ini memiliki tipe String dan Integer. String digunakan untuk bulan
pada x-axis dan Integer digunakan pada jumlah orang pada bulan tertentu.
o xAxis: Kita akan menggunakan ini untuk menambah String bulan.
2. Metode initialize() mengisi x-axis dengan daftar semua bulan.
3. Metode setPersonData(...) akan di akses oleh kelas MainApp untuk mengatur data
person. Ini akan mengulang melewati semua orang dan menghitung tanggal kelahiran
perbulan, kemudian ni menambah XYChart.Data .Data untuk tiap bulan ke seri data.
Tiap XYChart.Data Objek akan mewakilkan satu palang didalam bagan.
Menghubungkan Tampilan dan Pengendali.
1. Buka BirthdayStatistics.fxml di Scene Builder.
2. Pada kelompok Controller atur BirthdayStatisticsController sebagai pengendali.
3. Pilih BarChart kemudian pilih barChart sebagai properti fx:id (didalam kelommpok
Code).
84
4. Pilih CategoryAxis dan pilih xAxis sebagai prperti fx:id.
5. Kamu mungkin menambah judul ke BarChart (di kelompok Properties) untuk
membuat gaya lebih lanjut.
Menghubungkan Tampilan/Pengendali dengan MainApp
Kita akan menggunakan mekanisme yang sama untuk statistika tanggal lahir yang kita gunakan
untuk memperbaharui memperbaharui dialog person, dengan sebuah dialog munculan.
Tambahkan metode berikut ke kelas MainApp:
/**
* Opens a dialog to show birthday statistics.
*/
public void showBirthdayStatistics() {
try {
// Load the fxml file and create a new stage for the popup.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/BirthdayStatistics.fxml"
));
AnchorPane page = (AnchorPane) loader.load();
Stage dialogStage = new Stage();
dialogStage.setTitle("Birthday Statistics");
dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.initOwner(primaryStage);
Scene scene = new Scene(page);
dialogStage.setScene(scene);
85
// Set the persons into the controller.
BirthdayStatisticsController controller = loader.getController();
controller.setPersonData(personData);
dialogStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
Semuanya telah di atur, tetapi kita tidak meiliki apapun yang sebenarnya memanggil metode
showBirthdayStatistics(). Untungnya kita telah memiliki menu di RootLayout.fxml yang
bisa digunakan.
Tampilkan Menu Statistika Tanggal lahir
Di RootLayoutController tambah metode berikut yang akan menangani klik pengguna untuk
menampilkan bulir menu statistika tanggal lahir:
/**
* Opens the birthday statistics.
*/
@FXML
private void handleShowBirthdayStatistics() {
mainApp.showBirthdayStatistics();
}
Sekarang buka berkas RootLayout.fxml dengan Scene Builder. Buat Statistics Menu dengan
Show Statistics MenuItem:
86
Pilih Show Statistics MenuItem dan pilih handleShowBirthdayStatistics untuk On Action
(didalam kelompok Code)
Pada Eclipse, segarkan (F5) dan cobalah.
Informasi lebih lanjut pada bagan JavaFX
Sebuah tempat bagus untuk informasi adalah tutorial dari oracle Working with JavaFX Charts.
Berikutnya
Di tutorial terakhir Bagian 7 kita akan menyebarkan aplikasi kita (yaitu paket dan memberikan
aplikasi kepada pengguna kita)
87
Tutorial JavaFX 8 - Bagian 7: Penyebaran
May 10, 2014 • Updated Mar 12, 2015
Saya pikir saya akan menulis satu bagian terakhir dari seri tutorial ini untuk menunjukan
bagaimana untuk menyebarkan (ini berarti memaket dan publikasi) AddressApp.
Pembahasan di bagian 7
• Menyebarkan aplikasi JavaFX sebagai paket paket asli dengan e(fx)eclipse
Apa itu Penyebaran
Penyebaran adalah proses memaket dan mengantarkan perangkat lunak ke pengguna. Ini adalah
bagian penting dari pengembangan perangkat lunak semenjak hubungan pertama pengguna
dengan perangkat lunak kita.
Java beriklan dengan slogan Tulis sekali, jalankan dimanapun untuk menggambarkan
keuntungan dari bahasa java di gunakan lintas platform. Idealnya ini berarti aplikasi Java ktia
bisa berjalan di semua perangkat yang dilengkapi dengan Java Virtual Machine (JVM).
88
Di masa yang lampau, pengalaman pengguna untuk memasang aplikasi java tidak selalu
berjalan mulus, jika pengguna tidak memiliki versi java yang diperlukan, dia harus memasang
itu terlebih dahulu, ini membuat beberapa kesulitan seperti diperlukan hak admin, isu
kecocokan antara versi java, dan lain lain.
Untungnya JavaFX menyediakan pilihan penyebaran baru bernama Pemaketan asli (juga
disebut paket aplikasi tersendiri). Sebuah pemaketan asli merupakan bungkusan yang berisi
kode aplikasi dan java runtime (berdasar platform).
Dokumentasi resmi dari Oracle yang berisi panduan untuk semua kemungkinan JavaFX
deployment options.
Pada kirimian ini saya akan menunjukan bagaimana membuat paket asli dengan Eclipse dan
e(fx)clipse plugin.
Membuat Paket Alami
Tujuan membuat aplikasi mandiri dalam satu folder di komputer pengguna. Di tangkapan
layarn akan menampilkan bagaimana akan terlihat untuk AddressApp (di Windows):
Folder app berisi data aplikasi dan folder runtime berisi Java runtime berdasar platform.
Untuk membuat ini lebih nyaman bagi pengguna, kita akan menyediakan sebuah pemasangan
(installer):
• Sebuah exe file pemasangan untuk Windows
• Sebuah dmg file pemasangan untuk MacOs.
e(fx) plugin akan membantu kita menghasilkan paket alami dan pemasangan (installer).
89
Langkah 1- Perbaharui buld.fxbuild
Berkas build.fxbuild digunakan oleh e(fx)clipse untuk menghasilkan sebuah berkas yang
akan digunakan alat pembangun Ant(Ant Build). Jika anda tidak memiliki berkas
build.fxbuild, buat sebuah proyek JavaFX baru di Eclipse dan gandakan berkas yang
dihaslkan.
1. Buka build.fxbuild.
2. Isi semua bidang yang berisi bintang. Untuk MacOS, jangan gunakan spasi di judul
aplikasi, karena ini akan menyebabkan masalah.
3. Sebagai bentuk pemaketan pilih exe untuk Windows, dmg untuk MacOS, dan rpm
untuk Linux.
4. Klik pada tautan Generate ant build.xml only ((pada sisi kanan)e).
5. Verifikasi folder build baru dan sebuah berkas build.xml telah dibuat.
90
Langkah 2 - Tambahkan Ikon Pemasangan
Kita ingin memiliki ikon yang menarik untuk pemasang :
• AddressApp.ico untuk berkas ikon pemasang.
• AddressApp-setup-icon.bmp untuk pemasang ikon Splash scren.
• AddressApp.icns untuk ikon pemasang MacOs.
• AddressApp-volume.icns untuk ikon Mac
•
1. Buat subfolder didalam folder build:
o build/package/windows (hanya digunakan untuk windows)
o build/package/macosx (hanya digunakan untuk macos)
2. Salin ikon yang sesuai dari atas ke subfolder ini. Ini seharusnya terlihat seperti :
3. Penting: Nama ikon harus sama tepat dengan judul aplikasiyang telah di tentukan dalam
build.fxbuild:
o YourAppTitle.ico
o YourAppTitle-setup-icon.bmp
o YourAppTitle.icns
o YourAppTitle-volume.icns
Langkah 3 - Menambah sumber-sumber
Folder resources idak tersalin secara otomatis, kita harus secara manual menambahkanya ke
build direktori:
1. Buat subfolder berikut didalam folder build:
o build/dist
91
2. Salin folder resources (berisi gambar-gambar aplikasi) ke build/dist.
Langkah 4 - Perbaharui build.xml untuk memasukan ikon-ikon
E(fx)clipse telah menghasilkan berkas build/build.xml yang siap di eksekusi oleh Ant. Ikon
pemasang kita dan sumber gambar tidak hanyak langsung berfungsi.
E(fx)eclipse tidak bisa dikatkan untuk memasukan sumber-sumber tambahan seperti folder
resources dan beberapa ikon pemasangan yang telah kita tambahkan diatas, kita harus secara
manual memperbaharui build.xml:
Buka build.xml dan temukan jalur fxant. Tambahkan satu baru untuk ${basedir} (ini akan
membuat ikon pemasangan tersedia):
build.xml - add "basedir"
<path id="fxant">
<filelist>
<file name="${java.home}\..\lib\ant-javafx.jar"/>
<file name="${java.home}\lib\jfxrt.jar"/>
<file name="${basedir}"/>
</filelist>
</path>
Temukan blok fx:resources id="appRes" dibagian bawah dari berkas, tambah sebuah baris
untuk reources resources:
build.xml - add "resources"
<fx:resources id="appRes">
<fx:fileset dir="dist" includes="AddressApp.jar"/>
<fx:fileset dir="dist" includes="libs/*"/>
<fx:fileset dir="dist" includes="resources/**"/>
</fx:resources>
92
Entah bagaimana, nomor versi tidak akan ditambah di fx:application yang membuat
pemasang selalu baku ke versi 1.0 (seperti yang ditunjukan oleh beberpa orang di komentar).
Untuk menyelasaikan ini, secara manual tambah nomor versi:
build.xml - add "version"
<fx:application id="fxApplication"
name="AddressApp"
mainClass="ch.makery.address.MainApp"
version="1.0"
/>
Kita sudah bisa menjalankan build.xml sebagai Ant build pada saat ini. Ini dapat
menghasilkan berkas jar yang dapat dijalankan, tetapi kita ingin melangkah lebih maju dan
membuat installer yang bagus.
Langkah 5 (WINDOWS) - Window pemasang Exe
Dengan Inno Setup kita dapat membuat pemasang aplikasi Window sebagai sebuah berkas
.exe .exe yang dihasilkan akan melakukan pemasangan tingkat pengguna (tidak ada
memerlukan ijin admin), juga jalan pintas akan dibuat (menu atau Desktop).
1. Unduh Inno Setup 5 or later. Pasang Inno setup pada komputer anda, Ant Skrip kita
akan menggunakan ini secara otomatis untuk menghasilkan pemasangan.
2. Beritahu Windows mengenai jalur pemasangan pada Inno Setup (contoh C:\Program
Files (x86)\Inno Setup 5). Untuk melakukan ini, tambah Inno Setup pada variabel
Path di Variabel lingkungan Windows (Windows Environtment Variable). Jika anda
tidak tau dimana harus menemukanya, baca Bagaimana cara mengatur jalur dan variabel
lingkungan di Windows.
3. Mulai ulang Eclipse dan lanjut dengan langkah 6.
93
Langkah 5 (MAC) - MacOS pemasangan dmg
Unutk membuat MacOs dmg pemasangan seret dan lepaskan (drag-and-drop) tidak ada alat
tambahan yang diperlukan.
Catatan: Agar gambar pemasang dapat bekerja, harus memiliki nama yang sama dengan nama
aplikasi.
langkah 5 (LINUX) - Linux pemasangan rpm
Untuk pilihan pemaketan lain (msi untuk windows, rpm untuk Linux) lihat pemaketan alami
blog post atau ini oracle documentation.
Langkah 6 - Run build.xml
Sebagai langkah akhir, kita menjalankan build.xml dengan Ant: klik kanan pada berkas
build.xml Run As | Ant Build.
Proses ini akan memakan waktu (sekitar 1 menit pada komputer saya).
Jika semua telah berhasil, kamu seharusnya menemukan bundle alami di folder
build/deploy/bundles. Berikut adalah contoh versi window
94
Berkas AddressApp-1.0.exe dapat digunakan sebagai sebuah berkas pemasangan.
Pemasangan ini akan menyalin bundle ke
C:/Users/[yourname]/AppData/Local/AddressApp.
Berikutnya
Saya harap tutorial ini dapat membantu anda untuk memulai dengan JavaFX dan anda dapat
menulis proyek JavaFX anda dari sini.
Saya menghargai berbagai umpan balik. Jangan ragu untuk menulis komentar jika anda
memiliki bebrapa saran atau beberapa hal lain yang tidak jelas.