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

в 8:29, , рубрики: Песочница

Введение

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

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

О 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-файл проект.

Автор: MorrisDecker

Источник


  1. Борис:

    Здравствуйте, я очень позитивно воспринял Ваш материал. Сейчас хочу создать универсальную информационную систему. Для этого хочу использовать фреймы как набор атрибутов описания объектов и процессов, на основе которых создаю конкретные описания объектов или процессов. Некоторые атрибуты принимают собственные значения, а другие атрибуты просто ссылаются на существующие значения других атрибутов. Базу данных спроектировал на основе постгрескуль. Начал осваивать яву и пишу программки с ориентацией на TreeView. Могу ли я получить какую-то консультацию у вас по этой тематике.
    С уважением, Борис.

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js