Какой метод позволяет явным образом завершить работу графического приложения javafx
Однажды у меня возникла идея, написать небольшое настольное приложение для своих нужд — что-то типа небольшого словаря для изучения иностранных слов — и я начал ломать голову, а как бы мне это сделать? Естественно, первое, что мне пришло в голову — Swing. Все наверняка слышали о Swing . Это библиотека для создания пользовательских, графических интерфейсов. В связи с тем, что наш горячо любимый Oracle еще не полностью отказался от Swing, он не считается устаревшим, и приложения на нем по-прежнему работают. Однако он больше не модернизируется Swing, и ребята из Oracle дали нам понять, что за JavaFX будущее. Да и по сути, JavaFX использует компоненты Swing как поставщика услуг)
Что такое JavaFX?
Особенности JavaFX:
- JavaFX изначально поставляется с большим набором частей графического интерфейса, таких как всякие там кнопки, текстовые поля, таблицы, деревья, меню, диаграммы и т.д., что в свою очередь сэкономит нам вагон времени.
- JavaFX часто юзает стили CSS, и мы сможем использовать специальный формат FXML для создания GUI, а не делать это в коде Java. Это облегчает быстрое размещение графического интерфейса пользователя или изменение внешнего вида или композиции без необходимости долго играться в коде Java.
- JavaFX имеет готовые к использованию части диаграммы, поэтому нам не нужно писать их с нуля в любое время, когда вам нужна базовая диаграмма.
- JavaFX дополнительно поставляется с поддержкой 3D графики, которая часто полезна, если мы разрабатываем какую-то игру или подобные приложения.
- Stage — по сути это окружающее окно, которое используется как начальное полотно и содержит в себе остальные компоненты. У приложения может быть несколько stage, но один такой компонент должен быть в любом случае. По сути Stage является основным контейнером и точкой входа.
- Scene — отображает содержание stage (прям матрёшка). Каждый stage может содержать несколько компонентов — scene, которые можно между собой переключать. Внутри это реализуется графом объектов, который называется — Scene Graph (где каждый элемент — узел, ещё называемый как Node ).
- Node — это элементы управления, например, кнопки метки, или даже макеты (layout), внутри которых может быть несколько вложенных компонентов. У каждой сцены (scene) может быть один вложенный узел (node), но это может быть макет (layout) с несколькими компонентами. Вложенность может быть многоуровневой, когда макеты содержат другие макеты и обычные компоненты. У каждого такого узла есть свой идентификатор, стиль, эффекты, состояние, обработчики событий.
JavaFX: примеры использования
Знакомство с JavaFX SceneBuilder
Небольшое отступление. JavaFX уроки
- delete — по индексу удаляем выбранную(выделенную) собаку;
- edit — создаем новую собаку с переданными данными, и задаем ее вместо той которая была до этого;
- create — создаем новую собаку и дергаем метод вызова окна создания, передав новый объект, и после закрытия которого если имя не null, то сохраняем нового питомца.
Я хочу сохранить файл перед закрытием приложения JavaFX.
Вот как я настраиваю обработчик в Main::start :
И контроллер, вызывающий Stage::close при нажатии кнопки:
Однако при вызове Controller::exitApplication приложение закрывается без вызова обработчика (нет вывода).
Как я могу заставить контроллер использовать обработчик, который я добавил в primaryStage ?
4 ответа
- Создает экземпляр указанного класса Application
- Вызывает метод init()
- Вызывает метод start(javafx.stage.Stage)
- Ожидает завершения работы приложения, что происходит, когда происходит одно из следующих событий:
- приложение вызывает Platform.exit()
- последнее окно было закрыто и атрибут implicitExit на Platform равен true
- Вызывает метод stop()
Это означает, что вы можете вызвать Platform.exit() на своем контроллере:
До тех пор, пока вы переопределите метод stop() в основном классе для сохранения файла.
Как видите, при использовании stop() вам больше не нужно прослушивать запросы на закрытие для сохранения файла (хотя вы можете это сделать, если хотите предотвратить закрытие окна).
Ах, это известная ошибка в JavaFX, когда Stage не закрывается, если во время закрытия присутствует модальный диалог. Я свяжу вас с отчетом об ошибке, который я только что видел сегодня. Я думаю , что это исправлено в последней версии.
Разрешено в 8.4 он говорит. Я думаю, это то, что вы описываете.
Предположим, вы хотите спросить пользователя, хочет ли он выйти из приложения без сохранения работы. Если пользователь выберет «Нет», вы не сможете избежать закрытия приложения в рамках метода остановки. В этом случае вам следует добавить EventFilter в свое окно для события WINDOW_CLOSE_REQUEST.
В свой метод запуска добавьте этот код для обнаружения события:
(Обратите внимание, что вызов Platform.exit (); не запускает событие WindowEvent.WINDOW_CLOSE_REQUEST , см. Ниже, чтобы узнать, как запустить событие вручную с помощью настраиваемой кнопки)
Затем добавьте свою собственную логику. В моем примере я использую всплывающее окно с предупреждением, чтобы спросить пользователя, хочет ли он закрыть приложение без сохранения.
Метод event.consume() предотвращает закрытие приложения . Очевидно, вы должны добавить хотя бы кнопку, которая позволяет пользователю закрыть приложение, чтобы избежать принудительного закрытия приложения пользователем, что в некоторых случаях может повредить данные.
Наконец, если вам нужно запустить событие с помощью настраиваемой кнопки закрытия, вы можете использовать это:
Хотя есть и разные версии под разные java, желательно выбрать соответствующую вашей. Например, у нас в компьютерном классе всюду java 8, поэтому лучше скачать 8 версию.
Восьмая версия еще особенно хороша тем что ей не надо админских прав и можно ставить в компьютерных классах
по завершению установки запустится SceneBuilder
но он нам пока не нужен, так что можно его закрыть.
Делаем новый проект типа JavaFX
назову его CustomTypeGUI
получаем такую структуру проекта:
Принцип организации javafx интерфейса
Кликнем дважды на файлик sample.fxml чтобы открыть его и увидим там что-то такое:
fxml – это такой аналог html, только в рамках java, тут даже есть всякие импорты. И есть тэги, определяющие что будет отображаться. Тут к нас пока только один тэг GridPane, которые используется для выравнивания компонент по сетке.
Правда пока выравнивать нечего, так что пока это по сути пустой контейнер.
Особо стоит обратить внимание на атрибут fx:controller в нем указан класс вместе с полным путем, в котором мы будем писать код для интерфейса.
И так, попробуем запустить программу. Увидим что-то такое
Откуда же это все взялось? А на самом деле ничего хитрого нет. Открываем файлик Main.java и видим
Добавляем кнопку
Вернемся к файлу sample.fxml. Теперь запустим SceneBuidler, выбираем Open Project
и открываем наш файлик. Увидим что-то такое
Перетянем теперь кнопку на форму. Так как пока у нас пустой GridPane наша форма выглядит как пустота. Но тянуть можно:
Сохраним перетянутое Ctrl+S и переключимся обратно в Idea. И глянем что у нас выросло с файлике sample.fxml. Там у нас будет такое:
Видим, что тут много чего поменялось. Появились какие-то Constraints (это так называем ограничения на размеры колонок и строк, нам они пока не интересны).
Ну и главное, что тут появился тег children и внутри него наша кнопочка
- text – это то что написано на кнопке
- mnemonicParsing – это специальный тег для обработки горячих клавиш, но нам это сейчас неинтересно, так что можно игнорировать
Можно попробовать запустить:
ура, есть кнопочка! =)
Добавляем реакцию на нажатие
Но кнопка — это не кнопка если нажимая на нее ничего не происходит. Попробуем добавить реакцию на клик.
Переключимся на файл Controller.java:
Помните я упомянул атрибут fx:controller=”sample.Controller” внутри sample.fxml, так вот мы сейчас в том самом классе на который указывал этот атрибут. За счет него происходит связывание интерфейса и логики (контроллера).
Чтобы добавить реакцию на клик достаточно сделать следующие операции.
Можно даже попробовать запустить. И потыкать кнопку
хехе, очевидно ничего не произойдет. Мы ж пока функцию не привязали =)
А теперь переключимся на SceneBuilder и привяжем кнопку к функции
не забудем сохранить, и запустим проект по-новому
Кстати можно открыть файлик sample.fxml и глянуть что там произошло:
Читаем значение из текстового поля
Ну тыкать на кнопочки и выполнять всякие функции — это конечно важное умение. Но не менее важно уметь считать значение из поля, например, текстового.
Делается это в некотором роде проще чем привязка кнопки.
Например, хочу я добавить поле для ввода текста, и чтобы по нажатию на кнопку выводилось содержимое этого поля.
Что я сделаю? Сначала создам в классе поле, через которое образуется связь
теперь переключимся на SceneBuilder, перетащим текстовое поле на форму, а затем привяжем его к нашему классу, путем указания свойства fx:id:
если вдруг у вас в этом поле ничего не высветилось, то можете смело вписать туда вручную txtInput.
Сохраним в SceneBuilder и вернемся обратно в Idea. Если глянуть в файлик fxml то увидим
теперь вернемся в наш Controller и подправим метод showMessage:
Меняем значения текстового поля
О, давайте еще научимся изменять значение в списке, но тут все просто
В принципе вот и вся наука =)
- добавил поле в контроллер
- добавил объект в SceneBuilder
- связал через fx:id, либо связал с методом.
Теперь можно пилить что-нибудь посложнее.
Наверное, не будем рассматривать как делать конкретно приложение под вашу задачку. Все-таки в прошлом году мы уже пилили интерфейсы. И принцип должен быть понятен.
Но, очевидно, полезно рассмотреть, как можно добавлять разные компоненты и заставить их реагировать на ваши данные.
К тому же в JavaFX есть много хитрых особенностей, которые в WindowsForms не встречалось.
И так, попробуем добавить выпадающий список. Действуем по выше предложенному алгоритму. Сперва добавим поле в контроллер:
теперь добавляем на форму
затем подключаем по fx:id
не забываем сохраниться в SceneBuilder.
Запускаем и проверяем:
Ура! Есть список! Правда пустой пока…
Заполняем список (интерфейс Initializable)
И вот незадача, как же его заполнить? У нас по сути голый класс Controller, без ничего. Давайте попробуем в конструкторе:
запускаем и… бдыщь
суть стенаний виртуальной машины заключается в том, что comboBox нулевой. А нулевой он потому что привязка полей помеченных декоратором @FXML происходит уже после вызова конструктора, где-то в терньях фреймворка JavaFX.
Поэтому добавление элемента в конструкторе не прокатит.
Что ж делать? Оказывается все просто! Надо наследоваться от некого интерфейса Initializable и переопределить одну функцию, которую используется как своего конструктор формы. Делается это так
загоним теперь в метод initialize наш вызов добавления элемента
Добавляем реакцию на переключения списка
Давайте сначала добавим еще несколько элементов
Есть как минимум два способа добавить реакцию на переключения
Через SceneBuilder
не забываем сохраниться.
2) Затем в IDEA тыкаем правой кнопкой мыши на название метода
выбираем первый пункт Show Context Actions, а затем:
3) Нас перекинет в контроллер где мы увидим новодобавленный метод
в принципе, его можно было и руками добавить >_>
Этот метод будет вызываться каждый раз, когда мы будем переключать элемент списка. Давайте чего-нибудь там будем выводить:
Через лямбда-функцию
Ну тут просто в методе инициализации привязываете лямбда-функцию к реакции на изменение свойства.
Я на лекции расскажу что тут происходит на самом деле
Еще может пригодится чекбокс, для вывода всяких булевых значений. Добавим его на форму
добавим в Controller:
теперь подключим checkbox к форме
ну теперь мы можем, например, управлять им кликая на кнопку:
ой, забыли кнопке указать другую функцию
Вот теперь красота! =)
кстати если хотите текст поменять то правьте поле Text:
Одним из важных принципов при разработке приложений, а особенно GUI приложений заключается в том, что состояние формы (значения в полях, размеры и положение) после закрытия приложения и при последующем открытии должно сохранятся.
Как правило для хранения состояния приложения используют простые файлы в каком-нибудь распространённом формате типа *.xml, *.ini, *.json
Выбор моментов фиксации самого состояние остается на совести разработчика, это может быть момент сразу после изменения значений в полях, может быть реакцией на клик какой-нибудь кнопки (в идеале чем чаще, тем лучше), а может быть просто момент закрытия формы.
Подключаем обработчик закрытия формы
Мы будем сохранять в момент закрытия формы.
Вот мы недавно уже мучались с поиском момента создания формы, теперь так же помучаемся с определением момента закрытия формы.
К сожалению, тут простым интерфейсом не отделаешься. Вообще в JavaFX нет как такового понятия формы.
Там есть понятие stage (англ. состояние, сцена как место где происходят события), которое представляет собой окно приложения и scene (англ. тоже сцена, но уже в качестве события) которая, если немного упростить, представляет собой fxml файлик + контроллер.
Ну и ясно что именно к scene привязывается класс Controller. И так как scene за окно формы (то бишь stage) не отвечает, то из него и нельзя получить прямого доступа к событию закрытия окна.
Но можно получить не прямой. Для этого надо поймать событие закрытия формы (то бишь stage) и проксировать его в Controller. Делается это так:
Сначала пойдем в контроллер и создадим функцию которая должна будет вывязываться при закрытии формы. Такую:
теперь идем в Main и делаем магические пассы:
Сохраняем состояние формы в файл
И так, хотя данные на форме и так уже имеют привязанные поля, и можно считывать значения сразу с формы и сразу куда-то писать мы так делать не будем. Потому что в этом случае нам самим придется организовывать запись в файл, а затем еще учится парсить результат обратно. В общем, в современной разработке, это такой себе подход. Да и в прошлом семестре мы этого и так наелись.
Поэтому мы создадим класс который будет хранить состояние формы, а работу по сохранения/чтения его в файл отдадим на откуп встроенному в java XML сериализатору.
И так создаем новый класс
фиксируем в него поля
теперь идем в Controller и в методе закрытия формы прописываем:
а теперь добавляем сериализацию в файл. Тут все просто
теперь проверяем как работает
Так-так, файлик появился. У меня внутри такие строчки:
теперь попробуем добавить чтение из файла. Чтение стало быть делаем в инициализаторе:
Это моя первая статья, и я не жду, что она вызовет ажиотаж у публики, но надеюсь, что кому-то она будет интересна. Заранее извиняюсь за некорректные формулировки некоторых определений, ибо я пишу, как понимаю. Также хочу сразу извинится за ошибки которые мог совершить во время написания, ибо не Пушкин я, и пера не имею)
В этой статье речь пойдет о JavaFX. Это очень мощный, но пока молодой продукт от всеми любимой Oracle, который может вырасти титаном разработки приятных для глаза человеческого приложений. И я говорю не про “Helloworld” в консоли, а про красочное, анимационное, кроссплатформенное приложение, которое может поставить на место многие платформы по разработке пользовательских приложений.
Немного фактов:
«JavaFX — платформа для создания RIA(RichInternetApplication), позволяет строить унифицированные приложения с насыщенным графическим интерфейсом пользователя для непосредственного запуска из-под операционных систем, работы в браузерах и на мобильных телефонах, в том числе, работающих с мультимедийным содержимым».В общем, из определения которое взято из Википедии, можно понять, что это что-то непонятное, но крутое. Так оно и есть.
Коротко из истории:
В далеком 2007 на JavaOne компания SunMicrosystems представила миру первую версию платформы JavaFX. Для того, чтобы иметь возможность писать на этой платформе программы, вам было нужно ставить компилятор, и учить новый язык – JavaFXScript. Но все изменилось, когда Oracle купила SunMicrosystems. Компания решила прекратить развитие JavaFXScript, но сообщила, что 2 версия JavaFX будет портирована на Java. И уже 10 октября 2011 года была выпущена JavaFX версии 2.0, в виде библиотеки для Java. А с выпуском восьмой версии Java, она была добавлена в состав JDK и получила символический номер 8.
Возможности:
Среди возможностей этой платформы можно отметить: кроссплатформенность, поддержка каскадных таблицей стилей, поддержка анимации компонентов, возможность работы и отображение 3D графики, поддержка тачей и сенсоров и очень многое другое.
Чтобы лучше разобраться нужна, конечно же, практика. Давайте создадим новый проект. Что для этого нужно? Если вы уже используете Java 8, то можно выбрать вкладку JavaFX в окне File – NewProject(это для IntellijIDEA). Если же вы используете версию Java младше восьмой, вам нужно скачать библиотеку JavaFX из сайта Oracle, а потом импортировать в проект. В интернете есть полно информации как это все сделать.
Когда вы создали пустой проект, вы увидите 3 файла: Controller.java, sample.fxml, Main.java
Для начала нам не потребуется ни контроллер, ни файл з разметкой.
Начнем с простого примера.
В результате мы должны получить что-то следующее: в окне программы случайным образом разбросаны текcтовые поля, и пользователь имеет возможность перетаскивать эти обьекты в произвольные места.
Приступим. После создания проекта, ми увидим что-то следующее:
Давайте разбираться по порядку, класс Application является главным классом приложения, именно в его потоке вы можете использовать компоненты JavaFX, сейчас это звучит немного странно, но позже все станет понятно.
В методе main, где находится точка входа в программу, вызывается метод launch(), он в свою очередь, является точкой входа в FXприложение. Метод start() вызывается при создании потока приложения, в его параметрах можно увидеть объект класса Scene. Этот класс связан с экземпляром окна, которое будет видеть пользователь. Stageимеет большой набор методов, которые очень облегчают жизнь. Если знаете английский язык, то вы будете понимать интуитивно, что делает большинство методов.
Идем дальше, метод setScene(Scene), что он делает? Чтобы ответить на этот вопрос, нужно понять, что такое Scene. А Scene – это хранилище контента окна.
Начинаем писать. Напишем метод, который будет создавать сцену.
Для того, чтобы создать сцену, нужно указать главного родителя(root), куда можно будет добавлять элементы.
Итак, у нас это экземпляр класса Group, он наследуется от класса Parent, поэтому может содержать в себе другие элементы.
Он представляет собой NullLayoutиз Swing, то есть, какие координаты мы зададим элементу, то там он и будет рисоваться. Еще есть много контейнеров, некоторые представлены ниже.
- HBox(горизонтальная очередь элементов)
- VBox(вертикальная очередь элементов)
- FlowPane(многострочная горизонтальная очередь элементов)
- BorderPane(контейнер с 5 местами, сверху, снизу, слева, справа и по центру)
- GridPane(сетка элементов)
- ScrollPane(контейнер с возможностью прокрутки)
Это не все, но для начала этих хватит с головой.
Итак, для нашей первоначальной задачи нам нужно чтобы элементы не привязывались к своему местоположению, и для этого отлично подходит Group.
Давайте напишем метод, который будет создавать текстовое поле в случайной точке нашего окна.
В JavaFX есть очень полезные вещи, такие как билдеры, используя которые вы напишете значительно меньше кода для того чтобы придать вашему объекту нужные форму и свойства.
Теперь нужно добавить результат этого метода в наше приложение. Для этого нужно понимать, как устроен граф компонентов. Чтобы узнать список элементов внутри контейнера нужно обратиться к методу getChildren(), он вернет ObservableList, при изменении которого произойдет немедленная перерисовка контейнера. Поэтому, чтобы добавить
элемент в сцену, нужно его добавить в список детей одного из контейнеров.
Примерно так это и будет смотреться, теперь мы можем полюбоваться первым нашим результатом. Запускаем приложение и видим:
Теперь давайте сделаем так, чтобы мы могли перетаскивать этот текстовый блок по площади окна приложения. Для этого нужно отлавливать события. В JavaFX слушателей очень много.
Из всей этой кучи нам нужны будут только onMousePressed, onMouseReleased иonMouseDragged. Итак, первый слушатель должен оповестить нас, что пользователь нажал на нужный элемент, второй – о том, что пользователь притащил элемент на нужное место, ну и последний нужен для плавной картинки, чтобы элемент бежал за указателем мыши.
Далее представлен листинг метода, где цепляются все эти слушатели:
Вот и все, теперь у нас есть текстовое поле, которое можем таскать по всему окну. Ах да, только нужно вызвать этот метод для текстового поля.
И как вы могли заметить, этот метод будет работать не только для текстовых полей, а и вообще для всех классов, которые пошли от Node.
Вместо того чтобы таскать одну надпись, сделаем так, чтобы можно было таскать много, а для этого нужно просто добавить побольше полей в сцену.
Как вариант можно сделать так:
И в результате можно слепить что-то такое:
Ну и на последок, давайте сделаем возможность вращения этих полей. Как это будет выглядеть: когда пользователь нажимает правую кнопку мыши на объект, он может изменять угол поворота.
Для этого нужно немного изменить наш метод добавления слушателей:
После изменений имеем:
Запускаем, и лепим опять, вот что я получил в этот раз. Если ты дочитал сюда, а не пошел листать дальше новостную ленту, + 100 к карме тебе, и мою благодарность.
Вот и все, надеюсь кому-то эта статья понравилась, и это будет тогда только начало)
Читайте также: