
- •3.1 Лабораторна робота № 1. Створення JavaFx додатку. Авторизація користувачів.
- •Java Persistence api
- •3.1.1. Необхідне програмне забезпечення
- •3.1.2. Налаштування проекту JavaFx-додатку
- •3.1.3. Підключення бази даних
- •3.1.4. Створення моделей та їх маппінг за допомогою jpa
- •3.1.5. Створення допоміжного класу для роботи з моделями
- •3.1.6. Впровадження логіки авторизації користувача
- •3.2 Лабораторна робота № 2. Робота з медіа. Реалізація crud за допомогою jpa та TableView.
- •Медіа-програвач
- •Табличне представлення данних
- •3.2.1 Вдосконалення коду додатка
- •3.2.2 Створення інтерфейсу додатка
- •3.2.3 Створення медіа програвача
- •3.2.4 Впровадження можливості адміністрування користувачів
- •3.3 Лабораторна робота № 3. Використання вбудованого браузеру, будування графіків та створення анімації.
- •Акордеон
- •Анімація у JavaFx
- •Графіки
- •3.3.1 Інтегрування веб-браузеру до проекту
- •3.3.2 Створення графіків
- •3.3.3 Використання елементу акордіон для створення альтернативного варіанту панелі інструментів
- •3.3.4 Створення анімації кнопок
- •3.4 Лабораторна робота № 4. Технологія drag-and-drop у JavaFx-додатках. Використання стилів.
- •Drag-and-Drop
- •Стилювання додатку за допомогою css
- •3.4.1 Drag-and-Drop
- •3.4.2 Додавання можливості стилювання додатку
- •3.4.3 Створення стилів
- •3.5 Лабораторна робота № 5. Робота з JavaFx Scene Builder. 3-d трансформація у JavaFx. Розгортання додатку.
- •JavaFx Scene Builder
- •Трансформація у JavaFx
- •Розгортання JavaFx додатків
- •3.5.1 JavaFx Scene Builder
- •3.5.2 Створення логіки додатка
- •3.5.3 Розгортання додатка
3.2.4 Впровадження можливості адміністрування користувачів
Якщо користувач є адміністратором, то додаток буде надавати йому можливість адмініструвати користувачів, тобто робити над ними CRUD дії. Допоможе реалізувати CRUD механізм такий компонент JavaFx як TableView. Весь функціонал для роботи буде мати в собі новий клас CrudView, який необхідно створити в новому пакеті під назвою crud.
Рис. 3.10. Вигляд програвача з flv файлом
Спочатку в методі setButtonsActions потрібно додати виклик класу:
taskbar.getChildren().add(ButtonCreator.createButton(getClass().getResource("media/images/icon-4.png").toString(), new Runnable() {
public void run() {
changeView(new CrudView());
}));
Тепер потрібно розробити сам клас:
public class CrudView extends BorderPane{
private final HBox hbox = new HBox();
private final HBox buttonsBox = new HBox();
private final VBox vbox = new VBox();
private TableView<UserFx> table = new TableView();
private final int colwidth = 100;
private final GenericDAO<UserFx> userManager = new GenericDAO(UserFx.class);
private final ObservableList<UserFx> data = FXCollections.observableArrayList();
public CrudView(){
}
}
hbox, buttonsBox, vbox — ці елементи будуть представляти панель, яка міститиме в собі поля для створення нового користувача, та кнопки збереження змін або видалення.
TableView<UserFx> table — безпосередньо таблиця, яка буде відображати користувачів.
final int colwidth — значення ширини колонок.
В конструктор потрібно додати наступні рядки, які відповідають за створення таблиці з колонками для розміщення в них об’єктів моделі:
TableColumn username = new TableColumn("Username");
username.setPrefWidth(colwidth);
username.setCellValueFactory(new PropertyValueFactory<UserFx,String>("username"));
TableColumn phone = new TableColumn("Phone");
phone.setPrefWidth(colwidth);
phone.setCellValueFactory(new
PropertyValueFactory<UserFx,String>("phone"));
TableColumn country = new TableColumn("Country");
country.setPrefWidth(colwidth);
country.setCellValueFactory(new
PropertyValueFactory<UserFx,String>("country"));
TableColumn city = new TableColumn("City");
city.setPrefWidth(colwidth);
city.setCellValueFactory(new
PropertyValueFactory<UserFx,String>("city"));
TableColumn admin = new TableColumn("Admin");
admin.setPrefWidth(colwidth);
admin.setCellValueFactory(new
PropertyValueFactory<UserFx,Boolean>("admin"));
TableColumn banned = new TableColumn("Banned");
banned.setPrefWidth(colwidth);
banned.setCellValueFactory(new
PropertyValueFactory<UserFx,Boolean>("banned"));
data = FXCollections.observableArrayList(userManager.getAll());
table.setItems(data);
table.getColumns().addAll(username, phone, country, city, admin, banned);
this.setCenter(table);
Отже, для кожного поля моделі створюється окрема колонка, та встановлюється її ширина.
PropertyValueFactory — це фабрика, яка відповідає за наповнення колонки значеннями з колекції вхідних даних — data. Запустивши додаток можна подивитись на результат (рис. 3.11).
Як видно з рис. 3.11, поля Admin та Banned було б краще зробити чекбоксами. Але для цього потрібно розробити окремий клас та перевизначити CellFactory, який створює комірки для окремої колонки. Отже створіть новий клас з назвою BooleanCell в пакеті crud та помістить туди наступний код:
class BooleanCell extends TableCell<UserFx, Boolean> {
private CheckBox checkBox;
public BooleanCell() {
checkBox = new CheckBox();
checkBox.setDisable(false);
checkBox.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent e) {
commitEdit(checkBox.isSelected());
}
});
setAlignment(Pos.CENTER);
this.setGraphic(checkBox);
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
this.setEditable(true);
}
@Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (!isEmpty()) {
checkBox.setSelected(item);
commitEdit(item);
} else {
setGraphic(null);
}
}
}
Рис. 3.11. Таблиця з виведеними в ній записами з бази даних
В конструкторі класу створюється чекбокс та налаштовуються його параметри. Фабрика буде генерувати такі комірки для всієї таблиці, а не тільки для декількох записів, які будуть відображатись, отже потрібно в методі updateItem виправити код. Цей метод буде викликатися при авто-заповненні таблиці даними та при змінені значень і наперед потрібно викликати метод commitEdit(item), щоб потім за допомогою обробника подій спіймати цей момент та змінити значення елементу у колекції об’єктів, з якою працює таблиця і яку по закінченню роботи потрібно буде зберегти у базі даних.
Тепер в клас CrudView потрібно вставити наступні рядки:
Callback<TableColumn<UserFx, Boolean>, TableCell<UserFx, Boolean>> booleanCellFactory =
new Callback<TableColumn<UserFx, Boolean>, TableCell<UserFx, Boolean>>() {
@Override
public TableCell<UserFx, Boolean> call(TableColumn<UserFx, Boolean> p) {
return new BooleanCell();
}
};
TableColumn admin = new TableColumn("Admin");
admin.setPrefWidth(colwidth);
admin.setCellValueFactory(new PropertyValueFactory<UserFx,Boolean>("admin"));
admin.setCellFactory(booleanCellFactory);
TableColumn banned = new TableColumn("Banned");
banned.setPrefWidth(colwidth);
banned.setCellValueFactory(new PropertyValueFactory<UserFx,Boolean>("banned"));
banned.setCellFactory(booleanCellFactory);
Запустивши додаток можна подивитись на результат (рис. 3.12).
Тепер необхідно реалізувати можливість редагування значень в таблиці. Для цього користувачеві потрібно буде вибрати необхідне поле, потім необхідну колонку і ввести нове значення. Якщо він натисне Enter, то потрібно внести ці зміни до колекції, а якщо він натиснув Escape або натиснув вказівником мищі поза межами контрола, то значення не зберігається.
Рис. 3.12. Таблиця з чекбоксами у двох колонках
При початку редагування на місці комірки створюється textField, якщо його нема, або підставляється існуючий. Якщо вміст текстового поля змінювався, але не була натиснута клавіша Enter, то при повторному виклику редагування цієї ж комірки поле буде містити в собі введене незбережене значення, що є дуже комфортним для неуважних користувачів.
Для використання створеного класу в CrudView.java потрібно дописати наступне:
Callback<TableColumn, TableCell> cellFactory =
new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
return new EditingCell();
}
};
username.setCellFactory(cellFactory);
phone.setCellFactory(cellFactory);
country.setCellFactory(cellFactory);
city.setCellFactory(cellFactory);
table.setEditable(true);
В цьому ж файлі необхідно додати код, який відповідає за перенесення створених змін у колекцію. Для цього буде використаний оброблювач подій, який реагуватиме на виклик метода еditCommit.
phone.setOnEditCommit(new EventHandler<CellEditEvent<UserFx, String>>() {
@Override public void handle(CellEditEvent<UserFx, String> t) {
((UserFx)t.getTableView().getItems().get(
t.getTablePosition().getRow())).setPhone(t.getNewValue());
}
});
country.setOnEditCommit(new EventHandler<CellEditEvent<UserFx, String>>() {
@Override public void handle(CellEditEvent<UserFx, String> t) {
((UserFx)t.getTableView().getItems().get(
t.getTablePosition().getRow())).setCountry(t.getNewValue());
}
});
city.setOnEditCommit(new EventHandler<CellEditEvent<UserFx, String>>() {
@Override public void handle(CellEditEvent<UserFx, String> t) {
((UserFx)t.getTableView().getItems().get(
t.getTablePosition().getRow())).setCity(t.getNewValue());
}
});
username.setOnEditCommit(new EventHandler<CellEditEvent<UserFx, String>>() {
@Override public void handle(CellEditEvent<UserFx, String> t) {
((UserFx)t.getTableView().getItems().get(
t.getTablePosition().getRow())).setUsername(t.getNewValue());
}
});
banned.setOnEditCommit(new EventHandler<CellEditEvent<UserFx, Boolean>>() {
@Override public void handle(CellEditEvent<UserFx, Boolean> t) {
((UserFx)t.getTableView().getItems().get(
t.getTablePosition().getRow())).setBanned(t.getNewValue());
}
});
admin.setOnEditCommit(new EventHandler<CellEditEvent<UserFx, Boolean>>() {
@Override public void handle(CellEditEvent<UserFx, Boolean> t) {
((UserFx)t.getTableView().getItems().get(
t.getTablePosition().getRow())).setAdmin(t.getNewValue());
}
});
Запустивши додаток можна подивитись на результат (рис. 3.13).
Рис. 3.13. Демонстрація редагування значень таблиці
Тепер таблиця має непоганий вигляд, але єдине, що привертає увагу — це права крайня пуста колонка. В TableView є спеціальне поле, яке відповідає за політику змінення розміру колонок. Його значення потрібно змінити на CONSTRAINED_RESIZE_POLICY:
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
Після цих змін colwidth перестає бути потрібним, отже потрібно видалити всі рядки коду, які з ним пов’язані.
Наступним етапом є можливість створення нових користувачів. Для цього буде створена панель, яка буде знаходитися під таблицею. Також на панелі будуть знаходитися кнопки для видалення та збереження.
final TextField addUsername = new TextField();
addUsername.setPromptText("Username");
addUsername.setPrefWidth(username.getWidth());
final TextField addPhone = new TextField();
addPhone.setPromptText("Phone");
addPhone.setPrefWidth(phone.getWidth());
final TextField addCity = new TextField();
addCity.setPromptText("City");
addCity.setPrefWidth(city.getWidth());
final TextField addCountry = new TextField();
addCountry.setPromptText("Country");
addCountry.setPrefWidth(country.getWidth());
Поля будуть мати таку ж саму ширину, як і відповідні колонки. Отже при змінені ширини колонки, потрібно змінювати і ширину текстових полів. Для цього потрібно написати наступні оброблювачі подій, які використовують ChangeListener:
username.widthProperty().addListener(new ChangeListener(){
@Override public void changed(ObservableValue o,Object oldVal, Object newVal){
addUsername.setPrefWidth((Double)newVal);
}
});
phone.widthProperty().addListener(new ChangeListener(){
@Override public void changed(ObservableValue o,Object oldVal, Object newVal){
addPhone.setPrefWidth((Double)newVal);
}
});
city.widthProperty().addListener(new ChangeListener(){
@Override public void changed(ObservableValue o,Object oldVal, Object newVal){
addCity.setPrefWidth((Double)newVal);
}
});
country.widthProperty().addListener(new ChangeListener(){
@Override public void changed(ObservableValue o,Object oldVal, Object newVal){
addCountry.setPrefWidth((Double)newVal);
}
});
Через те, що текстові поля можуть змінювати свою довжину, а чекбокси — ні, то потрібно зробити для них спеціальну обгортку, яка не буде дуже виділятися серед інших елементів. Для цього новий клас ChboxWrapper (в пакеті crud) потрібно наслідувати від BorderPane. Потрібно щоб при відображені по центру цієї обгортки був напис з назвою колонки, а під цим написом чекбокс. Отже код класу виглядатиме наступним чином:
public class ChboxWrapper extends BorderPane{
final VBox vbox = new VBox();
public ChboxWrapper(CheckBox cb, String name, double width){
this.styleProperty().set("-fx-background-color: #FFFFFF;-fx-border-color: lightgray;-fx-border-width: 1; -fx-border-style: solid");
final Label label = new Label(name);
label.setFont(new Font("Arial", 8));
vbox.getChildren().add(label);
vbox.getChildren().add(cb);
vbox.setAlignment(Pos.CENTER);
this.setCenter(vbox);
this.setPrefWidth(width);
}
}
Тепер в конструкторі класу CrudView можна створити поля, що залишилися:
final CheckBox chAdmin = new CheckBox();
final ChboxWrapper chAdmWrapper = new ChboxWrapper(chAdmin, "Admin",admin.getWidth());
admin.widthProperty().addListener(new ChangeListener(){
@Override public void changed(ObservableValue o,Object oldVal, Object newVal){
chAdmWrapper.setPrefWidth((Double)newVal);
}
});
final CheckBox chBanned = new CheckBox();
final ChboxWrapper chBanWrapper = new ChboxWrapper(chBanned, "Banned", banned.getWidth());
banned.widthProperty().addListener(new ChangeListener(){
@Override public void changed(ObservableValue o,Object oldVal, Object newVal){
chBanWrapper.setPrefWidth((Double)newVal);
}
});
І додати в низ панелі класу CrudView:
hbox.getChildren().addAll(addUsername, addPhone, addCountry, addCity, chAdmWrapper, chBanWrapper);
vbox.getChildren().add(hbox);
this.setBottom(vbox);
Запустивши додаток можна подивитись на результат (рис. 3.14).
На панелі трохи нижче буде знаходитися три кнопки:
final Button delBut = new Button("Delete user");
final Button addBut = new Button("Add user");
final Button saveBut = new Button("Save users");
Рис. 3.14. Демонстрація панелі з полями для додавання нових користувачів
Обробник подій для додавання користувача:
addBut.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent e) {
UserFx ufx = new UserFx(
addUsername.getText(),
"newuser",
addPhone.getText(),
addCountry.getText(),
addCity.getText(),
chAdmin.isSelected(),
chBanned.isSelected()
);
userManager.create(ufx);
data.add(ufx);
addUsername.setText("");
addPhone.setText("");
addCity.setText("");
addCountry.setText("");
chAdmin.setSelected(false);
chBanned.setSelected(false);
}
});
Обробник подій для видалення користувача:
delBut.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent e) {
UserFx selected = table.getSelectionModel().getSelectedItem();
if (selected != null){
data.remove(selected);
userManager.delete(selected);
table.getSelectionModel().clearSelection();
}
}
});
Обробник подій для збереження зроблених змін:
saveBut.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent e) {
userManager.updateList();
}
});
І остається додати кнопки до панелі:
buttonsBox.getChildren().addAll(addBut, delBut, saveBut);
vbox.getChildren().add(buttonsBox);
Останню річ, яку потрібно виконати — це захист чекбоксів від ненавмисного змінення значень. Щоб змінити значення чекбоксу, користувачеві потрібно буде натиснути на необхідну комірку, і лише після цього буде доступне змінення значень контрола. Для цього в класі BooleanCell потрібно зробити такі зміни:
public BooleanCell() {
checkBox = new CheckBox();
checkBox.setDisable(false);
…
}
@Override
public void startEdit() {
super.startEdit();
checkBox.setDisable(false);
}
@Override
public void cancelEdit() {
super.cancelEdit();
checkBox.setDisable(true);
}
Запустивши додаток можна подивитись на результат (рис. 3.15).
Рис. 3.15. Демонстрація панелі з кнопками та неактивними чекбоксами
Практичне завдання
Дозволити доступ до можливості редагування користувачів тільки адміністраторам.
2. По закінченню редагування користувачів зробити перевірку на наявність хоча б одного не забаненого користувача з правами адміністратора.
Добавити валідацію в колонці Phone: дозволяти зберігати тільки цифри.
При додаванні нового користувача до таблиці зробити обов’язковим введення імені користувача та зробити валідацію на унікальність.
Контрольні питання
Що таке BorderPane і які її особливості?
Чим відрізняються VBox та HBox?
Які методи містить клас TableCell?
За що відповідає TableView.CONSTRAINED_RESIZE_POLICY?