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-ViewController)
1
•
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.
2
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).
3
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.
4
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-ViewController). 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)
5
•
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.
6
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).
7
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
8
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.
9
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).
10
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.
11
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.
12
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.
13
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.
14
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; 15
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(); 16
} 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; }
17
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.
18
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.
19
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
birthday;
/** * Default constructor. */ public Person() {
20
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
=
SimpleObjectProperty(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; 21
new
}
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(); }
22
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 birthdayProperty() { return birthday;
23
} }
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. */ 24
private
ObservableList
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 getPersonData() { return personData; }
// ... THE REST OF THE CLASS ...
25
=
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
setCellValueFactory(...)
IntegerProperty
atau
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 personTable; 26
DoubleProperty,
@FXML private TableColumn firstNameColumn; @FXML private TableColumn 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
27
* 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 28
-> 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(); } }
29
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.
30
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.
31
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
32
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("");
33
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 =
34
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
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; 35
{@link
} }
/** * 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:
36
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.
37
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.
38
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(); } }
39
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. 40
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 41
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.
42
* * @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()) {
43
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) {
44
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 { 45
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(...)!
46
•
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();
47
// 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. */
48
@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."); 49
=
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.
50
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.
51
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; }
52
.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;
53
-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; }
54
.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/ */ 55
.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 {
56
-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
57
stylesheet.
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.
58
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.
59
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 :
60
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.
61
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.
62
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 63
* 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:
64
Example xml output 1999-02-21 some city Hans Muster <postalCode>1234 <street>some street 1999-02-21 some city Anna Best <postalCode>1234 <street>some street
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. 65
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 persons;
@XmlElement(name = "person") public List getPersons() { return persons; }
public void setPersons(List persons) { this.persons = persons; } }
66
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"); 67
alert.setHeaderText("Could not load data"); alert.setContentText("Could
not
load
data
from
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); 68
file:\n"
+
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. 69
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. *
70
* @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());
71
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
FileChooser.ExtensionFilter( "XML files (*.xml)", "*.xml");
72
=
new
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: http://code.makery.ch"); alert.showAndWait(); }
/** * Closes the application. */ @FXML 73
Marco
Jakob\nWebsite:
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
RootLayoutController
di
Scene
Builder.
Dikelompok
Controller
pilih
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.
74
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(); } 75
// 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 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 */ 76
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. 77
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.
78
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.
79
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
BirthdayStatisticsController.java.
Mari kita lihat pada keseluruhan kelas pengendali sebelum saya mulai menjelaskan: 80
bernama
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>
FXCollections.observableArrayList(); 81
monthNames
=
/** * 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 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]++; }
82
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).
83
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); 84
// 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:
85
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)
86
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).
87
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).
88
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. 89
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
90
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">
Temukan blok fx:resources id="appRes" dibagian bawah dari berkas, tambah sebuah baris untuk reources resources: build.xml - add "resources"
91
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"
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 Path
ini, tambah Inno Setup pada variabel
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.
92
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
93
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.
94