Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
JavaFX.doc
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
4.88 Mб
Скачать

3.4.2 Додавання можливості стилювання додатку

Щоб надати користувачу можливість стилювати додаток, потрібно впровадити окремий елемент, в якому він зможе це зробити. Для цього буде створена нова кнопка та клас, в якому буде реалізовуватися логіка стилювання. Отже, спершу впровадимо кнопку:

private Node getStyleButton(){

return ButtonCreator.createButton(getClass().getResource("media/images/icon-5.png").toString(), new Runnable() {

public void run() {

changeView(new StyleView(getClass().getResource("media/images/backgrounds/").getPath(),getClass().getResource("media/styles/").getPath()));

}

});

};

private void setButtonsActions(HBox taskbar){

taskbar.getChildren().add(getStyleButton());

}

Рис. 3.30. Змінений фон за допомогою технології Drag-and-Drop

В конструктор класу StyleView передаються шляхи до директорій з фоновими зображеннями та css стилями. Цей клас даватиме можливість змінювати зображення із списку зображень, які знаходяться у відповідній директорії, змінювати базовий колір із списку з кольорами та змінювати файл зі стилями, теж зі списку.

Тепер потрібно створити клас StyleView в новому пакеті style.

public class StyleView extends BorderPane{

private final static int rectW = 50;

private final static int rectH = 100;

private final static int imgW = 200;

private final VBox vbox = new VBox();

private final HBox hbox = new HBox();

private final ListView<String> imgList = new ListView<String>();

private final ListView<String> colorList = new ListView<String>();

private final ChoiceBox cb = new ChoiceBox();

private final UserFx loggedUser = JavafxApp.getInstance().loggedUser;

private final ObservableList<String> colorData = FXCollections.observableArrayList("white", "chocolate", "salmon", "gold", "coral", "darkorchid", "darkgoldenrod", "lightsalmon", "black", "rosybrown", "blue", "blueviolet", "brown", "lightblue", "lightgreen", "lightgray", "gray");

public StyleView(String ImgDir, String StyleDir){}

rectW, rectH — висота та ширина квадратиків з кольорами у списку;

imgW — ширина зображень у списку;

vbox, hbox міститимуть в собі елементи для редагування зовнішнього вигляду додатку;

imgList — список із зображеннями;

colorList — список з кольорами;

cb — спадаючий список для зміни стилю;

loggedUser — поточний користувач;

colorData — колекція з кольорів, які будуть знаходитися у colorList.

Також потрібно змінити модель користувача, додавши туди два поля з геттерами та сеттерами, для збереження його базового кольору та шляху файлу стилів, і перестворити базу даних:

public class UserFx implements Serializable {

private String style;

private String baseColor;

public String getBaseColor() {

return baseColor;

}

public void setBaseColor(String baseColor) {

this.baseColor = baseColor;

}

public String getStyle() {

return style;

}

public void setStyle(String style) {

this.style = style;

}

}

Тепер можна поступово почати доповняти конструктор класу StyleView:

final Label imageLabel = new Label("Select background image");

final Label backLabel = new Label("Select base color");

final Label styleLabel = new Label("Select style file");

final ObservableList<String> imageData = getData(ImgDir);

final ObservableList<String> styleData = getData(StyleDir);

final Button saveButton = new Button("Save as default");

vbox.setAlignment(Pos.CENTER);

vbox.setSpacing(20);

hbox.setAlignment(Pos.CENTER);

hbox.setSpacing(20);

imgList.setItems(imageData);

colorList.setItems(colorData);

Для заповнення списку зображень та списку стилів використовується метод getData, який повертає колекцію шляхів до файлів, які знайдені у вказаній директорії. Код цього методу наступний:

private ObservableList<String> getData(String dirPath){

File dir = new File(dirPath);

final File[] files = dir.listFiles();

ObservableList<String> list = FXCollections.observableArrayList();

for (File file : files){

list.add(file.toURI().toString());

}

return list;

}

Після заповнення списків даними потрібно як і у TableView налаштувати відображення елементів. Для цього потрібно створити внутрішні класи, які будуть представляти ці комірки. Для списку кольорів клас буде виглядати наступним чином:

static class ColorRectCell extends ListCell<String> {

@Override

public void updateItem(String item, boolean empty) {

super.updateItem(item, empty);

Rectangle rect = new Rectangle(rectW, rectH);

if (item != null) {

rect.setFill(Color.web(item));

setGraphic(rect);

}

}

}

Інший клас комірки для списку буде пропорційно зменшувати зображення до вказаної ширини:

static class ImageCell extends ListCell<String> {

@Override

public void updateItem(String item, boolean empty) {

super.updateItem(item, empty);

ImageView iv = new ImageView();

iv.setImage(new Image(item));

iv.setFitWidth(imgW);

iv.setPreserveRatio(true);

setGraphic(iv);

}

}

Тепер потрібно в обох списках встановити фабрики, які будуть генерувати комірки за тільки-но створеними класами:

imgList.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {

@Override public ListCell<String> call(ListView<String> list) {

return new ImageCell();

}

});

colorList.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {

@Override public ListCell<String> call(ListView<String> list) {

return new ColorRectCell();

}

});

Якщо додати imgList та colorList у StyleView для відображання і запустити додаток, то можна побачити, що списки розтягуються на всю можливу довжину і є вертикально-орієнтованими. Наступні три рядки коду зроблять їх горизонтально-орієнтованими та зменшать ширину списку з кольорами:

imgList.setOrientation(Orientation.HORIZONTAL);

colorList.setOrientation(Orientation.HORIZONTAL);

colorList.setMaxWidth(rectW*8);

У списку, що випадає, стилі будуть відображатися як «Style N», де N — його порядковий номер. У відповідності до номерів існує колекція, яка зберігає шляхи до стилів.

for (int i = 0; i < styleData.size(); i++){

cb.getItems().add("Style "+i);

}

hbox.getChildren().addAll(styleLabel, cb, saveButton);

vbox.getChildren().addAll(imageLabel, imgList, backLabel, colorList, hbox);

this.setCenter(vbox);

Запустивши додаток можна побачити як виглядає розроблений інтерфейс (рис. 3.31).

Рис. 3.31. Вигляд інтерфейсу для стилювання додатка

Для видалення фонового зображення потрібно додати особливий малюнок з написом «none» і назвою none.jpg до папки з іншими зображеннями.

Тепер потрібно наповнити створений інтерфейс функціоналом. Для colorList та imgList він буде наступним:

colorList.getSelectionModel().selectedItemProperty().addListener(

new ChangeListener<String>() {

public void changed(ObservableValue<? extends String> ov, String old_val, String new_val) {

changeStyle("-fx-background-image: url('" + loggedUser.getBackground() + "');-fx-background-position: center center; -fx-background-repeat: stretch;-fx-background-size: contain; -fx-base: " + new_val + ";");

loggedUser.setBaseColor(new_val);

}

});

imgList.getSelectionModel().selectedItemProperty().addListener(

new ChangeListener<String>() {

public void changed(ObservableValue<? extends String> ov,

String old_val, String new_val) {

if (new_val.contains("none.jpg")){

changeStyle("-fx-background-image: url(''); -fx-base: " + loggedUser.getBaseColor() + ";");

loggedUser.setBackground("");

} else {

changeStyle("-fx-background-image: url('" + new_val + "');-fx-background-position: center center; -fx-background-repeat: stretch;-fx-background-size: contain; -fx-base: " + loggedUser.getBaseColor() + ";");

loggedUser.setBackground(new_val);

}

}

});

І метод, який використовується:

private void changeStyle(String style){

this.getScene().getRoot().setStyle(style);

}

Через те, що поведінка функції setStyle не є адитивною, потрібно встановлювати значення базового кольору та зображення фону разом, за один виклик. Значення обраного елементу одразу ж встановлюється в об’єкті поточного користувача. В обробнику подій списку зображень перевіряється назва обраного елементу і якщо зображення називається none.jpg, то значення шляху до зображення встановлюється пустим як у стилях, так і у поточного користувача.

Зміна файлу стилів буде відбуватися з використанням шляху до файлу стилів. Отже потрібно помістити стилі, які використовує JavaFX за замовчанням, у директорію, де і буде відбуватися їх пошук. Назва цього файлу caspian.css і знаходиться він у JavaFX runtime JAR файлі під назвою jfxrt.jar. Відкривши цей jar-файл за допомогою програми архіватора можна знайти необхідний файл caspian.css у com/sun/javafx/scene/control/skin/caspian/. Перемістивши caspian.css до файлів проекту, потрібно перейменувати його на style0.css.

Код обробника для чекбоксу cb, якій буде допомагати змінювати файли стилів, виглядатиме наступним чином:

cb.getSelectionModel().selectedIndexProperty().addListener(new

ChangeListener<Number>(){

public void changed(ObservableValue ov, Number value, Number new_value){ changeStyleSheet(styleData.get(new_value.intValue())); loggedUser.setStyle(styleData.get(new_value.intValue()));

}

});

І метод, який використовується:

private void changeStyleSheet(String stylePath){

this.getScene().getStylesheets().clear();

this.getScene().getStylesheets().add(stylePath);

}

Слід зазначити, що з версії JavaFX 2.1 будуть виправлені баги, пов’язані зі зміною файлів стилів, а саме — вся інформація з попереднього файлу буде повністю стиратися при використовуванні нового. У версіях до 2.1 можливі ситуації доповнення одних стилів властивостями інших.

Також потрібно додати декілька рядків, які будуть встановлювати обраним у спадаючому списку той стиль, який записаний в базі даних у користувача або, якщо запису нема — встановити перший, тобто «style 0», якому відповідає caspian.css:

if (!loggedUser.getStyle().equals("")){ cb.getSelectionModel().select(styleData.indexOf(loggedUser.getStyle()));

} else {

cb.getSelectionModel().selectFirst();

}

Для збереження всіх зроблених змін потрібно скористатися методом update менеджера моделей, передавши туди зміненого користувача:

saveButton.setOnAction(new EventHandler<ActionEvent>(){

public void handle(ActionEvent ae){

GenericDAO<UserFx> userManager = new GenericDAO(UserFx.class);

userManager.update(loggedUser);

}

});

Запустивши додаток, можна випробувати написану логіку (рис. 3.32).

Рис. 3.32. Демонстрація зміненого базового кольору

При тестуванні коду можна було побачити, що при зміненні сцени всі візуальні настройки пропадають, отже потрібно реалізувати їх завантаження. Метод gotoMain виглядатиме тепер наступним чином:

public void gotoMain(boolean accordion) {

BorderPane root = new BorderPane();

Scene scene = new Scene(root, mainW, mainH, Color.LIGHTBLUE);

scene.getStylesheets().add(loggedUser.getStyle());

stage.setScene(scene);

stage.sizeToScene();

stage.centerOnScreen();

view = new StackPane();

root.setCenter(view);

setupGestureTarget(scene);

if (accordion){

root.setLeft(getAccordion());

} else {

root.setBottom(getToolbar());

}

setBackgroundStyle(root, loggedUser.getBackground(), loggedUser.getBaseColor());

}

Отже, через особливість методу setStyle, метод setBackgroundImg став неактуальним, тому замість нього потрібно використовувати інший метод — setBackgroundStyle:

private void setBackgroundStyle(Node node, String url, String color){

node.setStyle("-fx-background-image: url('" + url + "');-fx-background-position: center center; -fx-background-repeat: stretch;-fx-background-size: contain;-fx-base: " + color + ";");

}

Також потрібно змінити виклик setBackgroundImg у методі обробки події перетягнутого зображення на:

setBackgroundStyle(stage.getScene().getRoot(), localCopy.toURI().toString(), loggedUser.getBaseColor());

Тепер кожного разу при зміні сцени будуть встановлюватися збереженні налаштунки стилів інтерфейсу користувача.