3 февраля 2013 в 12:29

Привет, JavaFX! Давай познакомимся? из песочницы

Введение


Приветствую, хабровчане. Недавно столкнулся с новой и интересной для меня технологией на Java – JavaFX. Душа моя тянется больше к русскому языку, чем к английскому, поэтому я приступил к поиску русскоязычных туториалов по данной технологии. Меня ждало разочарование, так как в сети достаточно мало гайдов на эту тему. Пришлось все изучать самому, читая нудную документацию.
Я хочу написать серию небольших гайдов для тех, кто только знакомится с данной технологией. Статьи будут рассчитаны для новичков и будут иметь «подробно-поясняющий» характер.

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

О JavaFX



JavaFX – достаточно молодая технология, которая должна была прийти и затмить собой всем известную технологию Swing. Со дня своего основания она перетерпела множество изменений и на данный момент мы имеем достаточно стабильную и функциональную версию. Я не работал с SilverLight, но, судя по комментариям, JavaFX существенно уступает продукту Microsoft.
Я выделяю два главных преимущества описываемой технологии:
  • упрощенное создание эффектов, анимации и других «красивых штучек»;
  • поддержка CSS стилей, с помощью которых внешний вид каждого компонента можно настраивать очень гибко.


О чем тут?


Данная статья вас только познакомит с JavaFX. Здесь не будут показаны даже основные возможности технологии, так как это будет в последующих статьях. Статья покажет вам, как создаются окна, как сделать их нестандартной формы и… все.

В конце мы получим окно, как на скриншоте ниже (черный фон — это мой рабочий стол) с одной работающей кнопкой.
Скриншоты

На кнопку наведена мышь:


Подготовка


Я пользуюсь средой разработки NetBeans, так как считаю ее достаточно удобной, красивой и мощной (и все это бесплатно). Чтобы не заморачиваться с установкой SDK, рекомендую просто скачать NetBeans с уже установленной платформой JavaFX.
Для тех, кто любит мышекликательное программирование, существует дополнительный инструмент, который называется JavaFX Scene Builder. После того, как вы его скачаете и установите, зайдите в NetBeans, в главном меню выберете «Сервис» -> «Параметры», далее переходите во вкладку «Java» -> «JavaFX». Там устанавливаете начальную страницу для Scene Builder путем выбора директории с Builder’ом. Теперь, после специальных манипуляций, вы сможете разрабатывать дизайн формы в данном редакторе.

Первый проект


Начнем создание нашего первого приложения. Выбираем в главном меню «Файл» -> «Создать проект». В представленных категориях выберем «JavaFX» и справа – «ПриложениеJavaFX»; далее все стандартно.
Созданный класс уже имеет некоторую функциональность. Ниже приведен код с комментариями.

Код
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class TestClass extends Application {
    /**
     * Метод, с которого начинается выполнение программы. Аналог main() в стандартной программе на Java.
     */
    @Override
    public void start(Stage primaryStage) {
        //Создание станартной кнопки.
        Button btn = new Button();
        //Присвое надписи кнопки следующего текста.
        btn.setText("Say 'Hello World'");
        //Установка обработчика события.
        btn.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                System.out.println("Hello World!");
            }
        });
        //Создание нового главного слоя/менеджера компоновки.
        StackPane root = new StackPane();
        //Добавление на него нашей кнопки.
        root.getChildren().add(btn);
        
        //Создание сцены. Выступает в роли контейнера (я ее приравниваю к панеле в Swing).
        Scene scene = new Scene(root, 300, 250);
        
        //primaryStage - объект нашего окна.
        //Устанавливаем заголовок окна.
        primaryStage.setTitle("Hello World!");
        //Устанавливаем текущую сцену
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * The main() method is ignored in correctly deployed JavaFX application.
     * main() serves only as fallback in case the application can not be
     * launched through deployment artifacts, e.g., in IDEs with limited FX
     * support. NetBeans ignores main().
     *
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
}


Создаем простенький проект


Теперь приступим к более сложной части, но и более интересной. Создадим небольшое окно входа в какое-либо приложение. Признаюсь честно: я его «выдрал» из своего курсового проекта, отдельно оно практической пользы не несет, но на нем я хочу показать несколько интересных моментов. Проект реализован на шаблоне MVC.
Ссылка на архив с необходимыми картинками.

Приступим


Создайте пакет «view». Кликаем ПКМ по созданному пакету, выбираем «Новый» -> «Класс Java». Называете, как желаете; я назову его «EnterScreen».
Наследуем его от класса «Application» и реализуем абстрактный метод.
Код
package view;
import javafx.application.Application;
import javafx.stage.Stage;
public class EnterScreen extends Application {    
    @Override
    public void start(Stage primaryStage) throws Exception {     
    }    
    public static void main(String[] args) {
        launch(args);
    }
}



Создадим в нашем классе поле (его назначение будет видно ниже).
Код
package view;

import javafx.application.Application;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class EnterScreen extends Application {
    Stage mainStage = null; 
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        mainStage  = new  Stage(StageStyle.TRANSPARENT);       
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}



Все действия по созданию окна я сделаю в одном методе start().Для начала я создам фон нашего окна и добавлю поля для ввода логина и пароля. Далее код с комментариями:
Код
package view;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class EnterScreen extends Application {
    public Stage mainStage = null;    
    @Override
    public void start(Stage primaryStage) throws Exception {
        //Создаем объект окна и при этом говорим, что оно будет без внешней границы и заголовка
        mainStage  = new  Stage(StageStyle.TRANSPARENT);
        //Создаем главный слой
        StackPane root = new StackPane();
        //screen - наш фон. Инициализируем его картинкой с прозрачностью.
        ImageView screen = new ImageView("pict/EnterScreen.png");
        //Добавляем наш фон на слой
        root.getChildren().add(screen);
        //Создаем еще один слой/менеджер компоновки
        AnchorPane anPane = new AnchorPane();
        //И добавляем его на главный слой
        root.getChildren().add(anPane);
        
        //Создаем текстовое поле и поле ввода пароля
        TextField login = new TextField("login");
        PasswordField password = new PasswordField();
        //Устанавливаем размеры наших полей
        login.setPrefSize(179, 24);
        password.setPrefSize(179, 24);
        //Теперь самое интересное: установка компонентов на свои места.
        //Следующие методы задают расположение компонентов путем смещения их от левой части окна. Значения в пикселях, но параметр вещественный
        AnchorPane.setLeftAnchor(login,519.0);
        AnchorPane.setLeftAnchor(password, 519.0);
        // Аналогично, только смещение идет от верха окна
        AnchorPane.setTopAnchor(login, 297.0);
        AnchorPane.setTopAnchor(password, 347.0);   
        //Теперь добавляем наши компоненты на слой
        anPane.getChildren().add(login);
        anPane.getChildren().add(password);   
        //Создаем сцену. Первый параметр: главный слой. Второй и третий: ширина и высота соответсвенно. Четвертый: цвет заливки (делаем его значением null,чтобы создать нестандартный вид формы)
        Scene scene = new Scene(root, 1024, 768, null);
        mainStage.setScene(scene);
        mainStage.show();        
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}



Создайте еще один пакет под названием «pict» и поместите туда картинки.
Это уже рабочая часть, только у нас еще нет кнопок. Но есть уже два поля ввода и окно нестандартного вида.
Создайте еще два пакета: «controller» и «model». В первом у нас будут обработчики событий, а во втором маленькая бизнес-логика.
Теперь добавим кнопку выхода (реализуем правую кнопку).
Код
package view;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class EnterScreen extends Application {
    public Stage mainStage = null;    
    @Override
    public void start(Stage primaryStage) throws Exception {        
        mainStage  = new  Stage(StageStyle.TRANSPARENT);  
        
        StackPane root = new StackPane();        
        ImageView screen = new ImageView("pict/EnterScreen.png");        
        root.getChildren().add(screen);        
        AnchorPane anPane = new AnchorPane();        
        root.getChildren().add(anPane);     
        
        TextField login = new TextField("login");
        PasswordField password = new PasswordField();
        
        login.setPrefSize(179, 24);
        password.setPrefSize(179, 24);
       
        AnchorPane.setLeftAnchor(login,519.0);
        AnchorPane.setLeftAnchor(password, 519.0);
       
        AnchorPane.setTopAnchor(login, 297.0);
        AnchorPane.setTopAnchor(password, 347.0);   
        
        anPane.getChildren().add(login);
        anPane.getChildren().add(password);  
        //Создаем картинку с изображением нашей кнопки (картинка имеет прозрачность)
        ImageView rightButton = new ImageView("pict/RightButton.png");
        //Добавляем обработчики событий для карттинки-кнопки
        rightButton.setOnMouseEntered(new EnterScreenEvents.OnMouseEnterRB());
        rightButton.setOnMouseExited(new EnterScreenEvents.OnMouseExitRB());
        rightButton.setOnMouseClicked(new EnterScreenEvents.OnMouseClickedRB(this));
        //Задаем расположение картинки-кнопки
        AnchorPane.setLeftAnchor(rightButton, 567.0);
        AnchorPane.setTopAnchor(rightButton, 420.0); 
        //Добавляем картинку-кнопку на слой
        anPane.getChildren().add(rightButton);
        
        Scene scene = new Scene(root, 1024, 768, null);
        mainStage.setScene(scene);
        mainStage.show();
        
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}



Наше приложение работать не будет, так как еще не созданы классы-обработчики. Что ж, давайте их создадим. В пакете «controller» создайте класс EnterScreenEvents. В данном классе создадим статические внутренние классы, которые и будут обработчиками. Все они будут реализовывать интерфейс «EventHandler». Далее следуйте по следующему коду:

Код
package controller;

import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.image.ImageView;
import view.EnterScreen;

public class EnterScreenEvents {
    public static class OnMouseEntered implements EventHandler{

        @Override
        public void handle(Event event) {
            ImageView iv = (ImageView) event.getSource();
            iv.setImage(resManager.getRightPressedButtonImage());
        }
        
    }
    public static class OnMouseExit implements EventHandler{

        @Override
        public void handle(Event event) {
            ImageView iv = (ImageView) event.getSource();
            iv.setImage(resManager.getRightButtonImage());
        }
        
    }
    public static class OnMouseClickedRB implements EventHandler{
        private EnterScreen ES = null;

        public OnMouseClickedRB(EnterScreen ES) {
            this.ES = ES;
        }
        
        @Override
        public void handle(Event event) {
            
        }
        
    }
}


Классы-обработчики созданы. Теперь реализуем модель. В модели мы сделаем менеджер ресурсов, у которого будут статические методы для загрузки изображений и возврата объектов с ними. Создайте в пакете «model» класс «resManager» и опишите, как показано ниже:
Код
package model;

import javafx.scene.image.Image;

public class resManager {
    public static Image getEnterScreenBackground(){
       return new Image("pict/EnterScreen.png"); 
    }
    
    public static Image getRightButtonImage(){
        return new Image("pict/RightButton.png");
    }
    
    public static Image getRightPressedButtonImage(){
        return new Image("pict/RightPressedButton.png");
    }

}


Теперь картинка-кнопка работает, но еще не закрывает окно. Не забудьте в представлении изменить жесткую ссылку фона на метод из модели. Теперь создадим метод в окне, который сможет его закрывать. Добавьте в класс такие строчки кода:
Код
    public void close() {
        Platform.runLater(new closing());
    }
    
    private class closing implements Runnable {

        @Override
        public void run() {
            mainStage.close();
        }
    }


Почему я организовал закрытие именно так? Другого пути я не нашел, так как в иных случаях выбрасывается исключение о недостатке привилегий у потока (только поток приложения и платформы может закрыть окно или выполнить другую операцию по смене состояния окна). Не забудьте добавить в обработчик клика мышки метод, закрывающий окно.

Заключение


Вот небольшое и нестандартное окно входа готово. Правда его функционал состоит только в том, чтобы оно закрывалось, но писать тут бесконечно тоже не получится. Если моя статья не будет опозорена в комментариях, то я продолжу цикл и расскажу про возможности технологии, уже более глубокие и эффективные: работа с CSS-стилями, основными компонентами, создание эффектов и анимации, а также многое другое.

Спасибо за внимание. До следующих встреч.

P.S> Отдельное спасибо Георгию Тихолазу (XMAITER) за создание кнопки и фонового рисунка.
P.S.S> Ссылка на экспортированный в ZIP-файл проект.
+3
5894
19
MorrisDecker –48,5

Комментарии (25)

0
hssergey, #
Еще бы неплохо увидеть скриншоты того что получается, чтобы не качать и собирать самостоятельно…
0
MorrisDecker, #
Учтено и исправлено:)
+3
SiPlus, #
Судя по шрифту на скриншотах, вы разрабатываете игру, в которой главный герой устанавливает инопланетянам Windows 3.11?
0
linuxoid, #
Ну вот. Я полез в статью раскрывать все expandable типа «Код». Не нашел ни одной картинки!.. Я разочарован! (disclaimer: текущий комментарий задумывался как шутка)
+4
valyard, #
Отличная статья! А теперь закопайте JavaFX откуда вы его выкопали!
0
MorrisDecker, #
Можно выслушать аргументы, почему вы так негативно относитесь к данной технологии?
0
mykir, #
Лично я, когда пытался реализовать один из проектов встретился с тем, что на тот момент (где-то 8 месяцев назад) не было достаточного количества компонентов и еще мне показалось достаточно сложно создавать кастомный стиль интерфейса.
0
Steamus, #
Кастомный стиль создаётся тривиальным CSS. Как один из вариантов. Что там показалось сложным?
0
MorrisDecker, #
8 месяцев назад эта технология была очень багнутая и трудоемкая. Сейчас все упрощено. Я читал комментарии с периода ее рождения и проследил все изменения. Она потерпела очень сильное изменение и сейчас является просто java фреймворком.
+1
solver, #
Какие аргументы? Вы смеетесь? Товарищ еще не вылез из криокамеры…
Скорее всего вообще не видел JavaFX старше 1.2, или видел но «не осилил» понять что видит )

Реально сейчас JavaFX2 это свет в конеце тонеля, для джавистов пишущих десктоп приложения. Ибо свинг хоть и хорош, был в свое время, но уже конечно давно устарел и его не развивают.
–1
lair, #
А зачем это нужно? Какая у этого практическая область применения?
+1
sha1dy, #
А может кто нибудь объяснить, почему javaFx умерла, не успев родиться? Почему так резко остановилось ее развитие?
+1
aspect, #
Откуда инфа? В семерке javafx уже встроена. В восьмерке выйдет javafx 8.
+1
Steamus, #
Она не умерла, а довольно быстро развивается. Был просто резкий переход после версии 1.3. До версии 1.3 включительно, использовался свой скриптовой язык для описания сцены. Когда Oracle купил Sun, они сделали резкий шаг, убрали этот язык и сказали что есть Java, к чему там ещё скриптинг таскать? И версия JavaFX 2 вышла уже без него, интенсивно развивается и уже стала частью JDK (по сути это замена Swing). И кстати, не очень корректно утверждение, что она слабее SilverLight. Я бы сказал, что корректнее сравнивать c WPF, И там уже решать. Предлагающие её закопать или её никогда не видели или имеют некое свое внутреннее эмоциональное предубеждение, неимеющее ничего общего с технической стороной вопроса.
0
Useless_guy, #
UI описывается в коде? Или есть разметка, наподобие XAML в WPF?
0
starleu, #
UI можно описывать как в коде, так и в разметке.
На первый взгляд, все это очень напоминает WPF и XAML.
К сожалению, это только на первый взгляд.
0
Steamus, #
А на второй? Что там не так?
0
senia, #
JavaFX, хоть и крайне перспективная разработка (очень надеюсь, что станет основным средством разработки GUI на java), но все еще крайне молода.

Таких понятий, как, например, DataTemplate и ControlTemplate, там нет и не предвидится в ближайшем будущем.

В других частях он просто другой. Не берусь говорить, что лучше или хуже.
0
Steamus, #
Так ведь они и не обязаны там быть. Одни и те же вещи можно делать разными способами. В JavaFX стилизация делается иначе. Просто JavaFX появилась позже чем WPF и у людей возникает соблазн искать в новой библиотеке полный технологический аналог уже знакомого им подхода. Не найдя его, они грят, что у-у-у… как всё запущено, надо закопать и тд и тп. Обычная эмоциональная реакция. А по сути, в JavaFX можно всё что угодно стилизовать через CSS. И это гораздо привычнее для многих.
0
senia, #
Так я и не говорю, что должны.
Я перечислил то, что мне бросилось в глаза в первую очередь. Меня не радует вместо ItemsControl + DataTemplate добавлять контролы по одному в коде — не декларативно это.

Вообще в JavaFX не хватает многих вещей, но все это можно списать на болезни роста.
0
Gorthauer87, #
А они FX Script выпилили окончательно уже?
0
Steamus, #
Да.
0
senia, #
И это замечательно!
Есть множество сторонних языков на jvm, совместимых с java. Незачем извращаться с еще одном, но уже от oracle.
Для примера можно взять ScalaFX.
0
Gorthauer87, #
Увы, он был покрасивше, чем эти XMLки из файла примеров. Собственно QML очень похожим сделали и он вполне удачно взлетел.
Увы, убили красивую фишку.
+1
Steamus, #
Так прямо в Java доступны все методы, что были в том скрипте. FXML — это альтернативный вариант описания. Как бы красива фишка не была, но если она избыточна по сути и требует времени на изучение, то она является тормозом. Зачем Java разработчику использовать некий, пусть и красивый, скрипт, если он может использовать не менее элегантный язык Java?

Только зарегистрированные пользователи могут оставлять комментарии.
Войдите, пожалуйста.