Віконце з кнопками на JavaFX:
Привіт!
Мої знання у створенні якогось графічного інтерфейсу до недавніх пір були нульовими. Тому було прийнято рішення трохи полазити простори Інтернету і створити якесь віконце з якими-небудь кнопками, за допомогою яких буде щось відбуватися. Можливо, в цьому тексті хтось знайде відповіді на виниклі коли питання; в той же час я сподіваюся на серйозну і грамотну критику.
Що буде відбуватися?
Так як мета у мене — розібратися з JavaFX, потрібно придумати якусь функцію у нашого вікна. Ідея така: створимо набір людей (припустимо, що вони працюють в якійсь компанії), для кожного з яких буде відомо ім’я, вік, зарплата і сімейний стан. Наше вікно, по-перше, буде виводити інформацію про кожного з них, по-друге, змінювати зарплату будь-якого співробітника, по-третє, мати можливість додавати нового співробітника, і, нарешті, в-четверных, здатний фільтрувати працівників в залежності від їх значень атрибутів. У результаті повинно вийти щось на зразок цього:
Починаємо
Насамперед створюємо головний клас Company, звідки відбуватиметься запуск додатка:
package company;
public class Company {
public static void main(String[] args) {
}
}
В цьому класі будемо розширювати можливості класу Application. З нього нам потрібен головним чином метод start(), в якому відбуватиметься основна дія (будемо пропускати помилки, що виникають в цьому методі; іншими словами, якщо щось піде не так, наше вікно не закриється, але в консолі з’явиться StackTrace помилки):
package company;
import javafx.application.Application;
import javafx.stage.Stage;
public class Company extends Application {
public void start(Stage primaryStage) throws Exception {
}
public static void main(String[] args) {
launch(args);
}
}
Відмінно. Для того, щоб працювати з представниками співробітників компанії, створимо клас User: об’єкти цього класу та будуть виступати в ролі співробітників.
Клас User
package company;
public class User {
User(String name, int age, int salary, boolean married) {
this.name = name;
this.age = age;
this.salary = salary;
this.married = married;
}
public void changeSalary(int x) {
salary = (salary + x <= 0)? 0: salary + x;
}
public String toString() {
return name;
}
public String getAge() {
return String.valueOf(age);
}
public String getSalary() {
return String.valueOf(salary);
}
public String getMarried() {
return (married)? ("married"):("single");
}
String name;
int age;
int salary;
boolean married;
}
Створюємо
Поки при запуску програма у нас не відбувається нічого. Щоб з’явилося хоча б віконечко, його необхідно додати. Це і зробимо.
package company;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Company extends Application {
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(root, WIDTH, HEIGHT);
primaryStage.setTitle("Company");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
Group root = new Group();
final private int WIDTH = 1000;
final private int HEIGHT = 600;
}
Що ми тільки що зробили: ми додали сцену (scene) розміром , дали їй назву «Company». На нашій сцені присутній група root, яка за своєю суттю є «контейнером», який буде містити в собі всі кнопки.
Додамо випадаючий список з нашими співробітниками. Створимо HBox, який покладемо наш випадаючий список ComboBox userBox і кнопку, яка буде видавати інформацію про співробітника (сам HBox помістимо в VBox):
package company;
import java.util.ArrayList;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Company extends Application {
public void start(Stage primaryStage) throws Exception {
users.add(new User("Alice", 20, 150, false));
users.add(new User("Bob", 34, 300, true));
users.add(new User("Peter", 18, 100, 'false'));
users.add(new User("Kate", 38, 300, true));
users.add(new User("Steve", 31, 250, true));
users.add(new User("Alan", 62, 500, true));
users.add(new User("Julia", 33, 320, true));
users.add(new User("Patric", 37, 300, true));
users.add(new User("Alexander", 34, 280, true));
users.add(new User("George", 28, 180, true));
users.add(new User("Mary", 22, 190, false));
userBox.getItems().addAll(users);
root.getChildren().add(strings);
strings.setPadding(new Insets(10, 30, 10, 30));
strings.setSpacing(20);
strings.getChildren().add(new Text("Select the user"));
strings.getChildren().add(buttonBox);
buttonBox.setSpacing(10);
buttonBox.getChildren().add(userBox);
Scene scene = new Scene(root, WIDTH, HEIGHT);
primaryStage.setTitle("Company");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
Group root = new Group();
VBox strings = new VBox();
HBox buttonBox = new HBox();
ComboBox<User> userBox = new ComboBox<>();
final private int WIDTH = 1000;
final private int HEIGHT = 600;
private ArrayList<User> users = new ArrayList<>();
}
Що таке VBox,HBox і ComboBox
HBox — це набір кнопок, текстів, полів для введення, кожен об’єкт якого розташований послідовно горизонтально. Аналогічний йому, VBox є тим же самим, але зберігає свої об’єкти (children) по вертикалі. Ми будемо використовувати наступну схему: создатим VBox, в який будемо класти кілька HBox’ів, кожен з яких покладемо послідовність кнопок та полів.
ComboBox — це сам просто випадаючий список.
Тепер ми хочемо створити команду, яка буде видавати інформацію про обраному співробітнику. Додамо саму кнопку (Button) і текстове поле (TextField), яке буде виводитися текст. Ці два об’єкти додамо в HBox:
package company;
/* */
public class Company extends Application {
public void start(Stage primaryStage) throws Exception {
/* */
buttonBox.setSpacing(10);
buttonBox.getChildren().add(userBox);
buttonBox.getChildren().add(buttonGetInfo);
buttonBox.getChildren().add(textInfo);
/* */
}
/* */
Button buttonGetInfo = new Button("Info");
Text textInfo = new Text();
/* */
}
Але поки ця кнопка нічого не робить. Щоб вона щось робила, їй необхідно призначити дію:
buttonGetInfo.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
User u = (User) userBox.getSelectionModel().getSelectedItem();
if (u != null) {
textInfo.setText("Age is" + u.getAge() + ", " +
"Salary is" + u.getSalary() + ", " +
"Relationship:" + u.getMarried());
} else {
textInfo.setText("User not selected");
}
}
});
Подальші дії
Абсолютно аналогічним чином додаємо ще два HBox’а: у другому буде відбуватися зміна зарплати, в третьому — добавляние нового співробітника. В силу того, що логіка тут абсолютно та ж сама, пропустимо пояснення цих моментів і відразу покажемо результат:
Код
package company;
import java.util.ArrayList;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Company extends Application {
public void start(Stage primaryStage) throws Exception {
users.add(new User("Alice", 20, 150, false));
users.add(new User("Bob", 34, 300, true));
users.add(new User("Peter", 18, 100, 'false'));
users.add(new User("Kate", 38, 300, true));
users.add(new User("Steve", 31, 250, true));
users.add(new User("Alan", 62, 500, true));
users.add(new User("Julia", 33, 320, true));
users.add(new User("Patric", 37, 300, true));
users.add(new User("Alexander", 34, 280, true));
users.add(new User("George", 28, 180, true));
users.add(new User("Mary", 22, 190, false));
userBox.getItems().addAll(users);
root.getChildren().add(strings);
strings.setPadding(new Insets(10, 30, 10, 30));
strings.setSpacing(20);
strings.getChildren().add(new Text("Select the user"));
strings.getChildren().add(buttonBox);
strings.getChildren().add(new Text("Change the salary"));
strings.getChildren().add(changeSalaryBox);
strings.getChildren().add(new Text("Add new User"));
strings.getChildren().add(addUserBox);
buttonBox.setSpacing(10);
buttonBox.getChildren().add(userBox);
buttonBox.getChildren().add(buttonGetInfo);
buttonBox.getChildren().add(textInfo);
changeSalaryBox.setSpacing(10);
changeSalaryBox.getChildren().add(buttonChangeSalary);
changeSalaryBox.getChildren().add(howMuchChange);
addUserBox.setSpacing(10);
addUserBox.getChildren().add(new Text("Name: "));
addUserBox.getChildren().add(name);
addUserBox.getChildren().add(new Text("Age: "));
addUserBox.getChildren().add(age);
addUserBox.getChildren().add(new Text("Salary: "));
addUserBox.getChildren().add(salary);
addUserBox.getChildren().add(new Text("Married: "));
addUserBox.getChildren().add(married);
addUserBox.getChildren().add(buttonAddUser);
buttonGetInfo.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
User u = (User) userBox.getSelectionModel().getSelectedItem();
if (u != null) {
textInfo.setText("Age is" + u.getAge() + ", " +
"Salary is" + u.getSalary() + ", " +
"Relationship:" + u.getMarried());
} else {
textInfo.setText("User not selected");
}
}
});
buttonChangeSalary.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
User u = (User) userBox.getSelectionModel().getSelectedItem();
if(u != null) {
u.changeSalary(Integer.parseInt(howMuchChange.getText()));
textInfo.setText("Age is" + u.getAge() + ", " +
"Salary is" + u.getSalary() + ", " +
"Relationshp:" + u.getMarried());
howMuchChange.clear();
}
}
});
buttonAddUser.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
String m = married.getText();
boolean mm = (m.equals("married"))? true:false;
User u = new User(name.getText(), Integer.parseInt(age.getText()),
Integer.parseInt(salary.getText()), mm);
users.add(u);
userBox.getItems().addAll(u);
name.clear();
age.clear();
salary.clear();
married.clear();
}
});
Scene scene = new Scene(root, WIDTH, HEIGHT);
primaryStage.setTitle("Company");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
Group root = new Group();
VBox strings = new VBox();
HBox buttonBox = new HBox();
ComboBox<User> userBox = new ComboBox<>();
Button buttonGetInfo = new Button("Info");
Text textInfo = new Text();
HBox changeSalaryBox = new HBox();
Button buttonChangeSalary = new Button("Change salary");
TextField howMuchChange = new TextField();
HBox addUserBox = new HBox();
Button buttonAddUser = new Button("Add User");
TextField name = new TextField();
TextField age = new TextField();
TextField salary = new TextField();
TextField married = new TextField();
final private int WIDTH = 1000;
final private int HEIGHT = 600;
private ArrayList<User> users = new ArrayList<>();
}
Фільтри
Тепер додамо «фільтрацію» співробітників за ознаками. Знову ж таки, списки для фільтрів будемо додавати з використанням ComboBox за тією ж самою логікою.
package company;
/* */
public class Company extends Application {
public void start(Stage primaryStage) throws Exception {
/* */
ageFilterBox.getItems().addAll(
"no matter",
"over 20",
"over 30",
"over 40"
);
salaryFilterBox.getItems().addAll(
"no matter",
"over 150",
"over 250",
"over 500"
);
relationshipFilterBox.getItems().addAll(
"no matter",
"married",
"single"
);
/* */
strings.getChildren().add(filters);
strings.getChildren().add(resultFilter);
/* */
filters.setSpacing(10);
filters.getChildren().add(new Text("Age"));
filters.getChildren().add(ageFilterBox);
filters.getChildren().add(new Text("Salary"));
filters.getChildren().add(salaryFilterBox);
filters.getChildren().add(new Text("Relationship"));
filters.getChildren().add(relationshipFilterBox);
filters.getChildren().add(filter);
/* */
filter.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
int age;
int index = ageFilterBox.getSelectionModel().getSelectedIndex();
age = (index == 0)? 0: (index == 1)? 21: (index == 2)? 31: 41;
int salary;
index = salaryFilterBox.getSelectionModel().getSelectedIndex();
salary = (index == 0)? 0 : (index == 1)? 151 : (index == 2)? 251:501;
boolean relate;
index = relationshipFilterBox.getSelectionModel().getSelectedIndex();
relate = (index == 1)? true: (index == 2)? false: true;
List<User> list;
if(index != 0) { list = users.stream().
filter(u -> u.age > age).
filter(u -> u.salary > salary).
filter(u -> u.married == relate).
collect(Collectors.toList()); }
else { list = users.stream().
filter(u -> u.age > age).
filter(u -> u.salary > salary).
collect(Collectors.toList()); }
String res = "";
for(u User: list) {
res += u.toString() + ", ";
}
resultFilter.setText(res);
}
});
/* */
}
/* */
HBox filters = new HBox();
ComboBox<String> ageFilterBox = new ComboBox<>();
ComboBox<String> salaryFilterBox = new ComboBox<>();
ComboBox<String> relationshipFilterBox = new ComboBox<>();
Button filter = new Button("filter");
Text resultFilter = new Text();
/* */
}
Готово!
Що це було
Сама по собі програма не являє ніякої корисності і була написана виключно для того, щоб познайомитися з JavaFX і її інструментами. У деяких місцях, наприклад, при зміні зарплати, не враховуються варіанти, коли не вибрано користувач, або коли введена рядок: в цих випадках буде з’являтися помилка. Те ж саме стосується полів, куди вводиться інформація при додаванні нових співробітників. І, нарешті, помилка буде виникати, якщо не вибрано якесь значення в будь-якому з випадаючих списків. При бажанні, кожен може додати свій «обхід» цих моментів.
При написанні фільтрів, до речі, я використовував відомі StreamAPI і -вирази, які скорочують код, додають в нього легкість розуміння і свій шарм.
Джерела:
- JavaFX 8 — Oracle Help Center
- Трохи про створення та історичних модифікаціях JavaFX
- Чудова стаття vedenin1980 про StreamAPI і
-вираження