Javafx как сделать окно модальным

Обновлено: 28.03.2024

A Dialog in JavaFX wraps a DialogPane and provides the necessary API to present it to end users. In JavaFX 8u40, this essentially means that the DialogPane is shown to users inside a Stage , but future releases may offer alternative options (such as 'lightweight' or 'internal' dialogs). This API therefore is intentionally ignorant of the underlying implementation, and attempts to present a common API for all possible implementations.

The Dialog class has a single generic type, R, which is used to represent the type of the result property (and also, how to convert from ButtonType to R, through the use of the result converter Callback ).

Critical note: It is critical that all developers who choose to create their own dialogs by extending the Dialog class understand the importance of the result converter property. A result converter must always be set, whenever the R type is not Void or ButtonType . If this is not heeded, developers will find that they get ClassCastExceptions in their code, for failure to convert from ButtonType via the result converter .

It is likely that most developers would be better served using either the Alert class (for pre-defined, notification-style alerts), or either of the two pre-built dialogs ( TextInputDialog and ChoiceDialog ), depending on their needs.

Once a Dialog is instantiated, the next step is to configure it. Almost all properties on Dialog are not related to the content of the Dialog, the only exceptions are contentTextProperty() , headerTextProperty() , and graphicProperty() , and these properties are simply forwarding API onto the respective properties on the DialogPane stored in the dialog pane property. These three properties are forwarded from DialogPane for developer convenience. For developers wanting to configure their dialog, they will in many cases be required to use code along the lines of dialog.getDialogPane().setExpandableContent(node) .

After configuring these properties, all that remains is to consider whether the buttons (created using ButtonType and the DialogPane.createButton(ButtonType) method) are fully configured. Developers will quickly find that the amount of configurability offered via the ButtonType class is minimal. This is intentional, but does not mean that developers can not modify the buttons created by the ButtonType that have been specified. To do this, developers simply call the DialogPane.lookupButton(ButtonType) method with the ButtonType (assuming it has already been set in the DialogPane.getButtonTypes() list. The returned Node is typically of type Button , but this depends on if the DialogPane.createButton(ButtonType) method has been overridden. A typical approach is therefore along the following lines:

Once a Dialog is instantiated and fully configured, the next step is to show it. More often than not, dialogs are shown in a modal and blocking fashion. 'Modal' means that the dialog prevents user interaction with the owning application whilst it is showing, and 'blocking' means that code execution stops at the point in which the dialog is shown. This means that you can show a dialog, await the user response, and then continue running the code that directly follows the show call, giving developers the ability to immediately deal with the user input from the dialog (if relevant).

JavaFX dialogs are modal by default (you can change this via the initModality(javafx.stage.Modality) API). To specify whether you want blocking or non-blocking dialogs, developers simply choose to call showAndWait() or show() (respectively). By default most developers should choose to use showAndWait() , given the ease of coding in these situations. Shown below is three code snippets, showing three equally valid ways of showing a dialog:

Option 1: The 'traditional' approach

Option 2: The traditional + Optional approach

Option 3: The fully lambda approach

There is no better or worse option of the three listed above, so developers are encouraged to work to their own style preferences. The purpose of showing the above is to help introduce developers to the Optional API, which is new in Java 8 and may be foreign to many developers.

Dialog Validation / Intercepting Button Actions

In some circumstances it is desirable to prevent a dialog from closing until some aspect of the dialog becomes internally consistent (e.g. a form inside the dialog has all fields in a valid state). To do this, users of the dialogs API should become familiar with the DialogPane.lookupButton(ButtonType) method. By passing in a ButtonType (that has already been set in the button types list), users will be returned a Node that is typically of type Button (but this depends on if the DialogPane.createButton(ButtonType) method has been overridden). With this button, users may add an event filter that is called before the button does its usual event handling, and as such users may prevent the event handling by consuming the event. Here's a simplified example:

Dialog Closing Rules

  • JavaFX dialogs can only be closed 'abnormally' (as defined above) in two situations:
    1. When the dialog only has one button, or
    2. When the dialog has multiple buttons, as long as one of them meets one of the following requirements:
      1. The button has a ButtonType whose ButtonBar.ButtonData is of type ButtonBar.ButtonData.CANCEL_CLOSE .
      2. The button has a ButtonType whose ButtonBar.ButtonData returns true when ButtonBar.ButtonData.isCancelButton() is called.
  • In all other situations, the dialog will refuse to respond to all close requests, remaining open until the user clicks on one of the available buttons in the DialogPane area of the dialog.
  • If a dialog is closed abnormally, and if the dialog contains a button which meets one of the two criteria above, the dialog will attempt to set the result property to whatever value is returned from calling the result converter with the first matching ButtonType .
  • If for any reason the result converter returns null, or if the dialog is closed when only one non-cancel button is present, the result property will be null, and the showAndWait() method will return Optional.empty() . This later point means that, if you use either of option 2 or option 3 (as presented earlier in this class documentation), the Optional.ifPresent(java.util.function.Consumer) lambda will never be called, and code will continue executing as if the dialog had not returned any value at all.

Property Summary

The dialog graphic, presented either in the header, if one is showing, or to the left of the content .

API to convert the ButtonType that the user clicked on into a result that can be returned via the result property.

Constructor Summary

Method Summary

All Methods Instance Methods Concrete Methods
Modifier and Type Method and Description
EventDispatchChain buildEventDispatchChain (EventDispatchChain tail)

The dialog graphic, presented either in the header, if one is showing, or to the left of the content .

API to convert the ButtonType that the user clicked on into a result that can be returned via the result property.

Sets the dialog graphic, which will be displayed either in the header, if one is showing, or to the left of the content .

Shows the dialog but does not wait for a user response (in other words, this brings up a non-blocking dialog).

Shows the dialog and waits for the user response (in other words, brings up a blocking dialog, with the returned value the users input).

Methods inherited from class java.lang.Object

Property Detail

dialogPane

The root node of the dialog, the DialogPane contains all visual elements shown in the dialog. As such, it is possible to completely adjust the display of the dialog by modifying the existing dialog pane or creating a new one.

contentText

A property representing the content text for the dialog pane. The content text is lower precedence than the content node , meaning that if both the content node and the contentText properties are set, the content text will not be displayed in a default DialogPane instance.

headerText

A property representing the header text for the dialog pane. The header text is lower precedence than the header node , meaning that if both the header node and the headerText properties are set, the header text will not be displayed in a default DialogPane instance.

graphic

The dialog graphic, presented either in the header, if one is showing, or to the left of the content .

result

A property representing what has been returned from the dialog. A result is generated through the result converter , which is intended to convert from the ButtonType that the user clicked on into a value of type R. Refer to the Dialog class JavaDoc for more details.

resultConverter

API to convert the ButtonType that the user clicked on into a result that can be returned via the result property. This is necessary as ButtonType represents the visual button within the dialog, and do not know how to map themselves to a valid result - that is a requirement of the dialog implementation by making use of the result converter. In some cases, the result type of a Dialog subclass is ButtonType (which means that the result converter can be null), but in some cases (where the result type, R, is not ButtonType or Void), this callback must be specified.

I am trying to use the example provided by @jewelsea at this gist and I'm stuck because I'm using FXML.

Where I'm stuck is in the code by jewelsea, where it says:

Whereas, since I'm using FXML, I do this:

Can you tell me how to modify my code (the 4 lines above), so it works with jewelsea's example?

1 Answer 1

The replacement code you are using via FXMLLoader rather than the WebView created scene sample code is fine, you don't need to modify it.

Your code will display the main scene for your primary stage based on an fxml document (for my sample I was using a WebView as my main scene, which you don't need, so you don't need any of the WebView related code from the gist).

What you need is some trigger in your controller at the time you want to display a dialog. As a simple example, you can setup an fxml for your main scene which just includes a button, and then supply an ActionHandler for the button in your controller (just like in the Introduction to FXML document).

Now, instead of just doing a println when the button is pressed as Introduction to FXML has, call the dialog.show() method like the gist did at the time of WebView document load. What should happen is that the dialog will now be displayed on top of your fxml generated scene.

You will notice that the dialog itself contains a Scene. If you wanted (and this is optional), you could define the scene contents using fxml. To do this, at the time of dialog construction, you set up a new fxml and new controller for the dialog contents and load the fxml created scene for the dialog into the dialog's stage. The code to do this is pretty much identical code to what you used to load the main scene's fxml into the primaryStage.

Try the above and see if it works for you. If you are still stuck I can probably create an fxml specific example of the gist along the lines of this answer.

Also note that the referenced gist was written a while ago, and there is a now a showAndWait method in JavaFX which facilitates blocking execution of code when making a dialog call and then allowing to process the result of the dialog without using some of the event handler mechanisms from the gist example. Strategies both with and without showAndWait are perfectly acceptable solutions though.

Хотя есть и разные версии под разные java, желательно выбрать соответствующую вашей. Например, у нас в компьютерном классе всюду java 8, поэтому лучше скачать 8 версию.

Imgur

Восьмая версия еще особенно хороша тем что ей не надо админских прав и можно ставить в компьютерных классах

Imgur

Imgur

Imgur

по завершению установки запустится SceneBuilder

Imgur

но он нам пока не нужен, так что можно его закрыть.

Делаем новый проект типа JavaFX

Imgur

назову его CustomTypeGUI

Imgur

получаем такую структуру проекта:

Imgur

Принцип организации javafx интерфейса

Кликнем дважды на файлик sample.fxml чтобы открыть его и увидим там что-то такое:

fxml – это такой аналог html, только в рамках java, тут даже есть всякие импорты. И есть тэги, определяющие что будет отображаться. Тут к нас пока только один тэг GridPane, которые используется для выравнивания компонент по сетке.

Правда пока выравнивать нечего, так что пока это по сути пустой контейнер.

Особо стоит обратить внимание на атрибут fx:controller в нем указан класс вместе с полным путем, в котором мы будем писать код для интерфейса.

И так, попробуем запустить программу. Увидим что-то такое

Imgur

Откуда же это все взялось? А на самом деле ничего хитрого нет. Открываем файлик Main.java и видим

Добавляем кнопку

Вернемся к файлу sample.fxml. Теперь запустим SceneBuidler, выбираем Open Project

Imgur

и открываем наш файлик. Увидим что-то такое

Imgur

Перетянем теперь кнопку на форму. Так как пока у нас пустой GridPane наша форма выглядит как пустота. Но тянуть можно:

Сохраним перетянутое Ctrl+S и переключимся обратно в Idea. И глянем что у нас выросло с файлике sample.fxml. Там у нас будет такое:

Видим, что тут много чего поменялось. Появились какие-то Constraints (это так называем ограничения на размеры колонок и строк, нам они пока не интересны).

Ну и главное, что тут появился тег children и внутри него наша кнопочка

  • text – это то что написано на кнопке
  • mnemonicParsing – это специальный тег для обработки горячих клавиш, но нам это сейчас неинтересно, так что можно игнорировать

Можно попробовать запустить:

Imgur

ура, есть кнопочка! =)

Добавляем реакцию на нажатие

Но кнопка — это не кнопка если нажимая на нее ничего не происходит. Попробуем добавить реакцию на клик.

Переключимся на файл Controller.java:

Помните я упомянул атрибут fx:controller=”sample.Controller” внутри sample.fxml, так вот мы сейчас в том самом классе на который указывал этот атрибут. За счет него происходит связывание интерфейса и логики (контроллера).

Чтобы добавить реакцию на клик достаточно сделать следующие операции.

Можно даже попробовать запустить. И потыкать кнопку

Imgur

хехе, очевидно ничего не произойдет. Мы ж пока функцию не привязали =)

А теперь переключимся на SceneBuilder и привяжем кнопку к функции

не забудем сохранить, и запустим проект по-новому

Кстати можно открыть файлик sample.fxml и глянуть что там произошло:

Читаем значение из текстового поля

Ну тыкать на кнопочки и выполнять всякие функции — это конечно важное умение. Но не менее важно уметь считать значение из поля, например, текстового.

Делается это в некотором роде проще чем привязка кнопки.

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

Что я сделаю? Сначала создам в классе поле, через которое образуется связь

теперь переключимся на SceneBuilder, перетащим текстовое поле на форму, а затем привяжем его к нашему классу, путем указания свойства fx:id:

если вдруг у вас в этом поле ничего не высветилось, то можете смело вписать туда вручную txtInput.

Сохраним в SceneBuilder и вернемся обратно в Idea. Если глянуть в файлик fxml то увидим

Imgur

теперь вернемся в наш Controller и подправим метод showMessage:

Меняем значения текстового поля

О, давайте еще научимся изменять значение в списке, но тут все просто

В принципе вот и вся наука =)

  • добавил поле в контроллер
  • добавил объект в SceneBuilder
  • связал через fx:id, либо связал с методом.

Теперь можно пилить что-нибудь посложнее.

Наверное, не будем рассматривать как делать конкретно приложение под вашу задачку. Все-таки в прошлом году мы уже пилили интерфейсы. И принцип должен быть понятен.

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

К тому же в JavaFX есть много хитрых особенностей, которые в WindowsForms не встречалось.

И так, попробуем добавить выпадающий список. Действуем по выше предложенному алгоритму. Сперва добавим поле в контроллер:

теперь добавляем на форму

затем подключаем по fx:id

Imgur

не забываем сохраниться в SceneBuilder.

Запускаем и проверяем:

Imgur

Ура! Есть список! Правда пустой пока…

Заполняем список (интерфейс Initializable)

И вот незадача, как же его заполнить? У нас по сути голый класс Controller, без ничего. Давайте попробуем в конструкторе:

запускаем и… бдыщь

Imgur

суть стенаний виртуальной машины заключается в том, что comboBox нулевой. А нулевой он потому что привязка полей помеченных декоратором @FXML происходит уже после вызова конструктора, где-то в терньях фреймворка JavaFX.

Поэтому добавление элемента в конструкторе не прокатит.

Что ж делать? Оказывается все просто! Надо наследоваться от некого интерфейса Initializable и переопределить одну функцию, которую используется как своего конструктор формы. Делается это так

загоним теперь в метод initialize наш вызов добавления элемента

Imgur

Добавляем реакцию на переключения списка

Давайте сначала добавим еще несколько элементов

Есть как минимум два способа добавить реакцию на переключения

Через SceneBuilder

Imgur

не забываем сохраниться.

2) Затем в IDEA тыкаем правой кнопкой мыши на название метода

Imgur

выбираем первый пункт Show Context Actions, а затем:

Imgur

3) Нас перекинет в контроллер где мы увидим новодобавленный метод

в принципе, его можно было и руками добавить >_>

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

Через лямбда-функцию

Ну тут просто в методе инициализации привязываете лямбда-функцию к реакции на изменение свойства.

Я на лекции расскажу что тут происходит на самом деле

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

добавим в Controller:

теперь подключим checkbox к форме


ну теперь мы можем, например, управлять им кликая на кнопку:

ой, забыли кнопке указать другую функцию


Вот теперь красота! =)

кстати если хотите текст поменять то правьте поле Text:


Одним из важных принципов при разработке приложений, а особенно GUI приложений заключается в том, что состояние формы (значения в полях, размеры и положение) после закрытия приложения и при последующем открытии должно сохранятся.

Как правило для хранения состояния приложения используют простые файлы в каком-нибудь распространённом формате типа *.xml, *.ini, *.json

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

Подключаем обработчик закрытия формы

Мы будем сохранять в момент закрытия формы.

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

К сожалению, тут простым интерфейсом не отделаешься. Вообще в JavaFX нет как такового понятия формы.

Там есть понятие stage (англ. состояние, сцена как место где происходят события), которое представляет собой окно приложения и scene (англ. тоже сцена, но уже в качестве события) которая, если немного упростить, представляет собой fxml файлик + контроллер.

Ну и ясно что именно к scene привязывается класс Controller. И так как scene за окно формы (то бишь stage) не отвечает, то из него и нельзя получить прямого доступа к событию закрытия окна.

Но можно получить не прямой. Для этого надо поймать событие закрытия формы (то бишь stage) и проксировать его в Controller. Делается это так:

Сначала пойдем в контроллер и создадим функцию которая должна будет вывязываться при закрытии формы. Такую:

теперь идем в Main и делаем магические пассы:

Сохраняем состояние формы в файл

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

Поэтому мы создадим класс который будет хранить состояние формы, а работу по сохранения/чтения его в файл отдадим на откуп встроенному в java XML сериализатору.

И так создаем новый класс

Imgur

фиксируем в него поля

теперь идем в Controller и в методе закрытия формы прописываем:

а теперь добавляем сериализацию в файл. Тут все просто

теперь проверяем как работает

Так-так, файлик появился. У меня внутри такие строчки:

теперь попробуем добавить чтение из файла. Чтение стало быть делаем в инициализаторе:

Java™ SE 6 has resolved modality issues that arose in earlier versions of the platform. The new modality model enables the developer to scope, or limit, a dialog box's modality blocking.

Before proceeding with the new modality model, review the following terms:

  • Dialog box — A top-level pop-up window with a title and a border that typically takes some form of input from the user. A dialog box can be modal or modeless. For more information about dialog boxes, see An Overview of Dialogs in the How to Make Dialogs page.
  • Modal dialog box — A dialog box that blocks input to some other top-level windows in the application, except for windows created with the dialog box as their owner. The modal dialog box captures the window focus until it is closed, usually in response to a button press.
  • Modeless dialog box — A dialog box that enables you to operate with other windows while this dialog box is shown.

In Java SE 6 the behavior of both modal and modeless dialog boxes has been changed so that they always appear on top of both not only of their parent windows and of all blocked windows as well.

The following modality types are supported in Java SE 6:

  • Modeless type — A modeless dialog box does not block any other window while it is visible.
  • Document-modal type — A document-modal dialog box blocks all windows from the same document, except windows from its child hierarchy. In this context, a document is a hierarchy of windows that share a common ancestor, called the document root, which is the closest ancestor window without an owner.
  • Application-modal type — An application-modal dialog box blocks all windows from the same application, except windows from its child hierarchy. If several applets are launched in a browser environment, the browser is allowed to treat them either as separate applications or as a single application. This behavior is implementation-dependent.
  • Toolkit-modal type — A toolkit-modal dialog box blocks all windows that run in the same toolkit, except windows from its child hierarchy. If several applets are launched, all of them run with the same toolkit. Hence, a toolkit-modal dialog box shown from an applet may affect other applets and all windows of the browser instance that embeds the Java runtime environment for this toolkit.

Additionally, you can set up the modality exclusion mode:

  • Exclusion mode — Any top-level window can be marked not to be blocked by modal dialogs. This property enables you to set up the modal exclusion mode. The Dialog.ModalExclusionType enum specifies the possible modal exclusion types.

Note : The new modality model does not implement a system modality, which blocks all applications (including Java applications) that are displayed on the desktop while a modal dialog box is active.

The ModalityDemo example demonstrates the first three of the four modality types mentioned above.

Four frames to demonstrate different modality types


This figure has been reduced to fit on the page.
Click the image to view it at its natural size.

  1. Click the Launch button to run ModalityDemo using Java™ Web Start (download JDK 7 or later). Alternatively, to compile and run the example yourself, consult the example index.
  2. The following dialog boxes will appear:
    • Book 1 (parent frame)
    • Book 2 (parent frame)
    • Feedback (parent frame)
    • Classics (excluded frame)
  3. Switch to the Book 1 frame and choose the Biography title for the book, then select OK.
  4. The Biography title will be displayed in the title of the dialog box. Enter the name, for example - “John”, into the text field.
  5. Switch to the Book 1 frame and change the title to Funny Tale, then select OK. Since the dialog box for entering the name is modeless, you can easily switch to its parent frame.

The following code snippet shows how to create dialog boxes of different modality types:

Find the demo's complete code in the ModalityDemo.java file.

In Java SE 6 you can create a document-modal dialog box without a parent. Because the Dialog class is a subclass of the Window class, a Dialog instance automatically becomes the root of the document if it has no owner. Thus, if such a dialog box is document-modal, its scope of blocking is empty, and it behaves as if it were a modeless dialog box.

The Modality API

The JDialog class constructors enable you to create dialog boxes of various modality types.

Constructor Purpose
JDialog(Dialog owner) Creates a modeless dialog box with the specified Dialog owner but without a title.
JDialog(Dialog owner, boolean modal) Creates a dialog box with the specified Dialog owner and modality.
JDialog(Dialog owner, String title) Creates a modeless dialog box with the specified Dialog owner and title.
JDialog(Dialog owner, String title, boolean modal) Creates a dialog box with the specified Dialog owner, title, and modality.
JDialog(Dialog owner, String title, boolean modal, GraphicsConfiguration gc) Creates a dialog box with the specified Dialog owner, title, modality, and graphics configuration.
JDialog(Frame owner) Creates a modeless dialog box without a title with the specified Frame owner. If the value for the owner is null, a shared, hidden frame will be set as the owner of the dialog box.
JDialog(Window owner, String title, Dialog.ModalityType modalityType) Creates a dialog box with the specified Window owner, title, and modality.

The following table lists methods inherited from the java.awt.Dialog class.


Следуйте за нами на нашей фан-странице, чтобы получать уведомления каждый раз, когда появляются новые статьи. Facebook

1- Window в JavaFX


Существует 3 вида модальости(modelity), которые вы можете применить для Stage через метод stage.initModality(Modelity).

  • Modelity.NONE
  • Modelity.WINDOW_MODAL
  • Modelity.APPLICATION_MODAL

При создании нового Stage, вы можете настроить для него родительское окно (Так же называется окно, которое владеет им), через метод stage.initOwner(parentStage).

NoМодальностьОписание
1Modelity.NONEКогда вы открываете новое окно с этой модальностью (modelity), новое окно будет независимым по отношению к родительскому окну. Вы можете интерактировать с родительским окном, или закрыть его не влияя на новое окно.
2Modelity.WINDOW_MODALКогда вы открываете новое окно с этой модальностью (modelity), новое окно блокирует родительское окно. Вы не можете интерактировать с родительским окном, до тех пор, пока это окно не закроется.
3Modelity.APPLICATION_MODALКогда вы открываете новое окно с этой модальностью (modelity), оно блокирует все другие окна приложения. Вы не можете интерактировать ни с каким окном, до тех пор пока это окно не закроется.

2- Например, откройте Modeless Window

Например, когда вы нажимете на Button на окне 1, он откроет второе окно с модальностью (modelity) по умолчанию (Modeless Window / Modelity.NONE).

3- Например, откроем Modal Window

Например, когда вы нажимете на Button на окне 1, он откроет новое окно с модальностью Modal Window (Modelity.WINDOW_MODAL). Когда еще отображается новое окно, вы не можете интерактировать с родительским окном.

Читайте также: