Pull to refresh

JavaFX 2.0 beta — пишем клиентское приложение на Java. На примере меню в Mac-стиле

Reading time8 min
Views26K
imageНа прошлогодней конференции JavaOne в Сан-Франциско компания Oracle анонсировала технологию JavaFX 2.0. А несколько дней назад мир увидел Java FX 2.0 Beta. JavaFX — это естественный шаг в эволюционном развитии клиентской платформы Java. Технология обеспечивает разработчиков кросс-платформенным инструментом для создания функционально насыщенных и привлекательных приложений.
Встроенная в технологию Java, JavaFX предлагает богатый графический и медийный API с поддержкой аппаратных графических ускорителей и большой выбор новых компонент: элементов управления, графиков, мультимедиа и встроенного браузера.
Из очевидных плюсов JavaFX 2.0 — возможность создания приложений без изучения новых технологий, применение привычных средств разработки и, конечно, все традиционные плюсы Java. Для корпораций — использование технологии Java на серверной и клиентской сторонах снизит интеграционные риски.
Из минусов: к сожалению, бета версия вышла только для Windows, но к релизу состав поддерживаемых платформ расширится.

Но лучше один раз увидеть, чем 100 раз услышать.
Давайте попробуем вместе написать панель задач с кнопками в стиле Mac

image

Первый шаг — устанавливаем JavaFX. Простейший способ — это скачать NetBeans plugin (http://www.oracle.com/technetwork/java/javafx/downloads/index.html ), в который будут включены JavaFX2.0, примеры и шаблоны. Если вы не пользуетесь NetBeans, то достаточно скачать SDK по той же ссылке и подключить jfxrt.jar из него как библиотеку к вашему проекту.

Итак, создаём простейшее окно с набором иконок. Подробности описаны в комментариях:

// Для работы с JavaFX нам необходимо наследовать класс Application
public class FXUIDemo extends Application {

    public static void main(String[] args) {
        // это наша точка старта -- этот метод поднимает FX стек и загружает туда наше приложение
        Application.launch(args);
    }

    private HBox taskbar;

    @Override
    public void start(Stage stage) {
       
        // переданный в параметре объект stage является нашим окном
        stage.setTitle("FX Demo");

        // здесь мы создаём сцену, которая является содержимым окна и layout manager для неё
        BorderPane root = new BorderPane();
        Scene scene = new Scene(root, 720, 550, Color.LIGHTGRAY);
        stage.setScene(scene);

        // создадим отдельный layout для иконок-кнопок -- horizontal box
        taskbar = new HBox(10);
        taskbar.setPadding(new Insets(10, 30, 50, 30));
        taskbar.setPrefHeight(150);
        taskbar.setAlignment(Pos.CENTER);
        root.setBottom(taskbar);

        // и добавим сами кнопки из подготовленных картинок
        for (int i = 0; i < 5; i++) {
            ImageView node = new ImageView(new Image(getClass().getResource("icon-" + i + ".png").toString()));
            taskbar.getChildren().add(node);
        }

        stage.setVisible(true);
    }
}

Вот результат работы этого кода:

image

Теперь оживим кнопки.

Для этого выделим их создание в отдельный метод, добавим анимацию при проведении мыши, отражение и анимацию нажатия на кнопку.

В данном случае анимацию будет осуществлять класс ScaleTransition. Выглядит это так:

 
        ScaleTransition animationGrow = new ScaleTransition(Duration.valueOf(300), node);
        animationGrow.setToX(1.3);
        animationGrow.setToY(1.3);
        animationGrow.play();


В данном коде указываем ScaleTransiton, что в через 300ms объект node должен стать в 1.3 раза больше начального. Больше ничего не требуется, все промежуточные значения класс ScaleTransition просчитает и применит к объекту node сам. При желании можно управлять плавностью изменения значения, FPS и другими параметрами.
Набор аналогичных классов Transition имеется для анимации на базе других свойств объектов JavaFX: размера, местоположения, угла наклона, прозрачности и т.п.

Теперь добавим отражение:

node.setEffect(new Reflection());


Базовые настройки нас вполне устроят, поэтому кроме конструктора эффекта ничего вызывать не надо.
После добавления обработчиков мыши функция createButton() будет выглядеть так:

private static final double SCALE = 1.3; // коэффициент увеличения
    private static final double DURATION = 300; // время анимации в мс

    private Node createButton(String iconName, final Runnable action) {
        // загружаем картинку
        final ImageView node = new ImageView(new Image(getClass().getResource(iconName).toString()));

        // создаём анимацию увеличения картинки      
        final ScaleTransition animationGrow = new ScaleTransition(Duration.valueOf(DURATION), node);
        animationGrow.setToX(SCALE);
        animationGrow.setToY(SCALE);

        // и уменьшения
        final ScaleTransition animationShrink = new ScaleTransition(Duration.valueOf(DURATION), node);
        animationShrink.setToX(1);
        animationShrink.setToY(1);

        // добавляем эффект отраженичя
        final Reflection effect = new Reflection();
        node.setEffect(effect);

        // обработчик нажатия мыши
        node.setOnMouseClicked(new EventHandler<MouseEvent>() {

            public void handle(MouseEvent event) {
                action.run();
            }
        });
        // при наведении курсора мы запускаем анимацию увеличения кнопки
        node.setOnMouseEntered(new EventHandler<MouseEvent>() {

            public void handle(MouseEvent event) {
                node.toFront();
                animationShrink.stop();
                animationGrow.playFromStart();
            }
        });
        // когда курсор сдвигается -- запускаем анимацию уменьшения
        node.setOnMouseExited(new EventHandler<MouseEvent>() {

            public void handle(MouseEvent event) {
                animationGrow.stop();
                animationShrink.playFromStart();
            }
        });

        return node;
    }


Теперь давайте добавим обработчик нажатия на кнопку. В маковской панели задач она при этом затемняется на короткое время. Мы для реализации такого поведения используем эффект ColorAdjust и класс Timeline. Обычно Timeline применяется для решения задач анимации, которым не хватает функционала Transitions. Используя Timeline, разработчик может выбирать, какие свойства объектов будут изменяться с течением времени, задавая ключевые точки. Но мы пока ограничимся использованием Timeline в качестве таймера для отмены эффекта:

// создаём эффект затемнения        
        final ColorAdjust effectPressed = new ColorAdjustBuilder().brightness(-0.5).build();
        node.setOnMouseReleased(new EventHandler<MouseEvent>() {

            public void handle(MouseEvent event) {
                // в обработчике нажатия применяем эффект. Тут имеется следующая тонкость: это уже второй эффект для кнопки,
                // поэтому мы его выставляем не напрямую, а как input для первого эффекта
                effect.setInput(effectPressed);
                // создаём Timeline, который через 300 мс отключит затемнение.
                new TimelineBuilder().keyFrames(new KeyFrame(Duration.valueOf(300), new EventHandler<ActionEvent>() {

                    public void handle(ActionEvent event) {
                        effect.setInput(null);
                    }
                })).build().play();
                action.run();
            }
        });


Стоит отметить, что Timeline и ColorAdjust были созданы с помощью шаблона builder. Подобные builders добавлены почти для всех объектов JavaFX2.0, что позволяет писать более компактный код и избегать использования лишних переменных.

Вот что у нас получилось. На изображении курсор над четвертой кнопкой. На скриншоте, к сожалению, не показать анимации, поэтому, надеюсь, у вас будет возможность и желание попробовать самим.

image

Но не будем останавливаться на достигнутом. Иконки для кнопок были выбраны неслучайно, и далее на каждую кнопку я буду добавлять соответствующие компоненты JavaFX.

Для начала добавим на нашу сцену место для размещения новых компонент.

// StackPane -- это layout manager, который располагает объекты в центре выделенного ему пространства
        StackPane view = new StackPane();
        root.setCenter(view);
        view.getChildren().add(new Text("Hello from JavaFX..."));


Далее стираем цикл создания кнопок, теперь мы их будет создавать по одной вместе с содержательными обработчиками нажатия.

Итак, первая кнопка — медиафайл.

В тело функции start() добавляем одну строчку для создания Media Player и вызов функции createButton() для его запуска.

mediaPlayer = new MediaPlayer(new Media("http://webcast-west.sun.com/oow2010.flv"));
    taskbar.getChildren().add(createButton("icon-0.png", new Runnable() {
            public void run() {
                changeView(new MediaView(mediaPlayer));
                mediaPlayer.play();
            }
        }));


Для сокращения кода обработчиков нажатия на последующие кнопки была добавлена фунция changeView() и вынесены переменные mediaPlayer и view.

private StackPane view;
        private MediaPlayer mediaPlayer;

       private void changeView(Node node) {
           view.getChildren().clear(); // очищаем view
           mediaPlayer.stop(); // останавливаем медиаплеер, если он работает
           view.getChildren().add(node); // добавляем во view новый элемент
       }


Получаем

image

Вторая кнопка — графики.

В JavaFX2.0 было добавлено 6 различных видов графиков. Подробно с ними можно ознакомиться в демо-приложении ChartsSampler, поставляющимся вместе с SDK.
Мы по нажатию на вторую кнопку будем создавать Line Chart.
Для этого потребуется чуть больше кода, чем для медиаплеера. Необходимо настроить оси координат, дать всем элементам графика имена и, конечно же, создать данные для графика.
Тем не менее, всё это укладывается в 15-20 строчек кода.

taskbar.getChildren().add(createButton("icon-1.png", new Runnable() {

            public void run() {
                // оси координат
                NumberAxis xAxis = new NumberAxis();
                NumberAxis yAxis = new NumberAxis();
                // график
                LineChart<Number, Number> chart = new LineChart<Number, Number>(xAxis, yAxis);
                chart.setTitle("Basic LineChart");
                xAxis.setLabel("X Axis");
                yAxis.setLabel("Y Axis");
                // набор случайных данных
                XYChart.Series<Number, Number> series = new XYChart.Series<Number, Number>();
                series.setName("Random Data");
                Random random = new Random();
                for (int i = 0; i < 10 + random.nextInt(20); i++) {
                    series.getData().add(new XYChart.Data<Number, Number>(10 * i + 5, random.nextDouble() * 120));
                }
                chart.getData().add(series);
                changeView(chart);
            }
        }));


image

Хотя мы это и не используем, стоит отметить, что графики уже анимированы и добавление новых элементов будет приводить к плавному сдвигу старых данных.

На следующую кнопку повесим ещё один новый элемент управления со смешным названием Accordion. Он представляет собой набор раздвигающихся панелей, довольно часто встречающийся в последнее время элемент интерфейса.

taskbar.getChildren().add(createButton("icon-2.png", new Runnable() {

            public void run() {
                Accordion accordion = new Accordion();
                for (int i = 0; i <= 4; i++) {
                    TitledPane t1 = new TitledPane(new Label("Image " + i),
                            new ImageView(new Image(getClass().getResource("icon-" + i + ".png").toString())));
                    accordion.getPanes().add(t1);
                }
                changeView(accordion);
            }
        }));


На скриншоте, к сожалению, опять не видно анимации: панели раскрываются не скачком, а плавно и красиво.

image

Четвёртая кнопка: WebView — встраиваемый браузер. Эта компонента предоставляет собой полноценный браузер, основанный на WebKit, поддерживающий CSS, JavaScript, DOM и HTML5.

Добавляем

taskbar.getChildren().add(createButton("icon-3.png", new Runnable() {
            public void run() {
                WebView web = new WebView(new WebEngine("http://habrahabr.ru"));
                changeView(web);
            }
        }));


и видим habrahabr.ru внутри JavaFX.

image

Наконец для последней, пятой кнопки, я оставил ещё две замечательные возможности JavaFX2.0 — связывание (binding) и CSS-стили.

Binding позволяет связать между собой свойства практически любых двух объектов JavaFX. Разработчикам не нужно заботиться о создании listeners и синхронизации, достаточно написать одну строчку кода для связывания свойств. Например, вот так:

Slider slider = new Slider();
    Circle circle = new Circle();
    circle.radiusProperty().bind(slider.valueProperty());


Теперь при изменении положения бегунка контрола slider, радиус круга circle будет автоматически изменяться, и круг будет перерисовываться.
Конечно же, binding не ограничен банальным прямым связыванием. Вы можете создавать формулы, изменять типы данных, применять двустороннее связывание, но это уже тема для отдельной статьи.

Также у каждого элемента сцены JavaFX есть стили, которые можно объединять в классы и задавать в файлах CSS или выставлять напрямую.
В компоненте для пятой кнопки мы объединим эти две функциональности: мы создадим компоненту список с различными стилями и через binding свяжем стиль нашей панели задач с выбранным элементом списка.

taskbar.getChildren().add(createButton("icon-4.png", new Runnable() {

            public void run() {
                // создаём список
                ListView listView = new ListView();
                // заполняем его стилями
                listView.setItems(FXCollections.observableArrayList(
                        "-fx-background-color: green;",
                        "-fx-background-color: linear (0%,0%) to (100%,100%) stops (0.0,aqua) (1.0,red);",
                        "-fx-background-color: transparent;",
                        "-fx-opacity: 0.3;",
                        "-fx-opacity: 1;"));
                // через binding связываем стиль панели задач с выбранным элементом списка
                taskbar.styleProperty().bind(listView.getSelectionModel().selectedItemProperty());
                changeView(listView);
            }
        }));


Вот, например, как будет выглядеть панель задач с градиентом на фоне.

image

Вот и всё. Мы написали менее чем 200 строк кода и создали довольно симпатичный образец UI.

В заключение несколько ссылок:
— Код приложения из статьи: http://www.javaone.ru/data/FXUIDemo.zip
— Видео с приложением в действии: http://www.youtube.com/watch?v=IHhA8G-0C9M
— По адресу http://download.oracle.com/javafx/ располагаются статьи с обзором основной функциональности
— Кроме того в состав SDK и Netbeans bundle входят демонстрационные приложения вместе с исходниками. Особенно должны быть интересны:
— Ensemble — огромная коллекция примеров со встроенным javadoc и code view.
— ChartsSampler — демонстрация возможностей чартов, причём достаточно необычных.

Статья подготовлена Сергеем Гриневым и Александром Белокрыловым
Tags:
Hubs:
Total votes 50: ↑41 and ↓9+32
Comments18

Articles