Введение
Приветствую, хабровчане. Недавно столкнулся с новой и интересной для меня технологией на 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-файл проект.