Перейти к содержимому

Groovy nodebuilder как добавить атрибут в узел

  • автор:

How to set node attribute dynamically using NodeBuilder pattern in groovy

Preferably it would be nice to reference the current element in the conditional statement since the condition might appear deep down in the structure.

asked Aug 26, 2011 at 12:56
10.3k 6 6 gold badges 46 46 silver badges 71 71 bronze badges

1 Answer 1

The easiest way to do this is to evaluate the condition for the dynamic attribute outside the node closure. For Example:

if (condition) < b.div(attribute: "value", dynamicAttribute: true) < . >> else < b.div(attribute: "value") < . >> 

Alternatively, you can create a map of the attributes beforehand:

def attributes = [attribute: "value"] if (condition) < attributes['dynamicAttribute'] = true >b.div(attributes)

Работа с XML в Groovy

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

В этом руководстве мы покажем, как добавлять, редактировать или удалять элементы из XML в Groovy, используя различные подходы. Мы также покажем, как создать структуру XML с нуля .

2. Определение модели

Давайте определим структуру XML в нашем каталоге ресурсов, которую мы будем использовать в наших примерах:

 articles>   article>   title>First steps in Javatitle>   author id="1">   firstname>Sienafirstname>   lastname>Kerrlastname>   author>   release-date>2018-12-01release-date>   article>   article>   title>Dockerize your SpringBoot applicationtitle>   author id="2">   firstname>Jonasfirstname>   lastname>Lugolastname>   author>   release-date>2018-12-01release-date>   article>   article>   title>SpringBoot tutorialtitle>   author id="3">   firstname>Danielefirstname>   lastname>Fergusonlastname>   author>   release-date>2018-06-12release-date>   article>   article>   title>Java 12 insightstitle>   author id="1">   firstname>Sienafirstname>   lastname>Kerrlastname>   author>   release-date>2018-07-22release-date>   article>   articles> 

И прочитайте его в переменную InputStream :

 def xmlFile = getClass().getResourceAsStream("articles.xml") 

3. XML-парсер

Начнем изучение этого потока с класса XmlParser .

3.1. Чтение

Чтение и анализ XML-файла, вероятно, является наиболее распространенной XML-операцией, которую приходится выполнять разработчику. XmlParser предоставляет очень простой интерфейс, предназначенный именно для этого:

 def articles = new XmlParser().parse(xmlFile) 

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

Давайте теперь реализуем простой тест, используя Spock , чтобы проверить правильность нашего объекта article:

 def "Should read XML file properly"()    given: "XML file"    when: "Using XmlParser to read file"   def articles = new XmlParser().parse(xmlFile)    then: "Xml is loaded properly"   articles.'*'.size() == 4   articles.article[0].author.firstname.text() == "Siena"   articles.article[2].'release-date'.text() == "2018-06-12"   articles.article[3].title.text() == "Java 12 insights"   articles.article.find  it.author.'@id'.text() == "3" >.author.firstname.text() == "Daniele"   > 

Чтобы понять, как получить доступ к значениям XML и как использовать выражения GPath, давайте сосредоточимся на внутренней структуре результата операции XmlParser#parse .

Объект article является экземпляром groovy.util.Node . Каждый узел состоит из имени, карты атрибутов, значения и родителя (который может быть нулевым или другим узлом) .

В нашем случае значением article является экземпляр groovy.util.NodeList , который является классом-оболочкой для коллекции Node s. NodeList расширяет класс java.util.ArrayList , который обеспечивает извлечение элементов по индексу. Чтобы получить строковое значение узла, мы используем groovy.util.Node#text().

В приведенном выше примере мы ввели несколько выражений GPath:

  • article.article[0].author.firstname — получить имя автора первой статьи — article.article [n] будет напрямую обращаться к n -й статье .
  • ‘*’ — получить список дочерних элементов статьи — это аналог groovy.util.Node#children()
  • author.’@id’ — получить атрибут id элемента author . author.’@attributeName’ обращается к значению атрибута по его имени (эквиваленты: author[‘@id’] и author.@id )

3.2. Добавление узла

Как и в предыдущем примере, давайте сначала прочитаем содержимое XML в переменную. Это позволит нам определить новый узел и добавить его в наш список статей, используя groovy.util.Node#append.

Давайте теперь реализуем тест, который доказывает нашу точку зрения:

 def "Should add node to existing xml using NodeBuilder"()    given: "XML object"   def articles = new XmlParser().parse(xmlFile)    when: "Adding node to xml"   def articleNode = new NodeBuilder().article(id: '5')    title('Traversing XML in the nutshell')   author    firstname('Martin')   lastname('Schmidt')   >   'release-date'('2019-05-18')   >   articles.append(articleNode)    then: "Node is added to xml properly"   articles.'*'.size() == 5   articles.article[4].title.text() == "Traversing XML in the nutshell"   > 

Как видно из приведенного выше примера, процесс довольно прост.

Заметим также, что мы использовали groovy.util.NodeBuilder, который является хорошей альтернативой использованию конструктора узла для нашего определения узла .

3.3. Изменение узла

Мы также можем изменить значения узлов с помощью XmlParser . Для этого давайте еще раз проанализируем содержимое XML-файла. Далее мы можем отредактировать узел содержимого, изменив поле значения объекта Node .

Давайте помнить, что хотя XmlParser использует выражения GPath, мы всегда извлекаем экземпляр NodeList, поэтому для изменения первого (и единственного) элемента мы должны получить к нему доступ, используя его индекс.

Давайте проверим наши предположения, написав быстрый тест:

 def "Should modify node"()    given: "XML object"   def articles = new XmlParser().parse(xmlFile)    when: "Changing value of one of the nodes"   articles.article.each  it.'release-date'[0].value = "2019-05-18" >    then: "XML is updated"   articles.article.findAll  it.'release-date'.text() != "2019-05-18" >.isEmpty()   > 

В приведенном выше примере мы также использовали Groovy Collections API для обхода NodeList .

3.4. Замена узла

Далее давайте посмотрим, как заменить весь узел, а не просто изменить одно из его значений.

Аналогично добавлению нового элемента, мы будем использовать NodeBuilder для определения узла , а затем заменим в нем один из существующих узлов с помощью groovy.util.Node#replaceNode :

 def "Should replace node"()    given: "XML object"   def articles = new XmlParser().parse(xmlFile)    when: "Adding node to xml"   def articleNode = new NodeBuilder().article(id: '5')    title('Traversing XML in the nutshell')   author    firstname('Martin')   lastname('Schmidt')   >   'release-date'('2019-05-18')   >   articles.article[0].replaceNode(articleNode)    then: "Node is added to xml properly"   articles.'*'.size() == 4   articles.article[0].title.text() == "Traversing XML in the nutshell"   > 

3.5. Удаление узла

Удаление узла с помощью XmlParser довольно сложно. Хотя класс Node предоставляет метод remove(Node child) , в большинстве случаев мы не стали бы использовать его сам по себе.

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

По умолчанию доступ к вложенным элементам с помощью цепочки ссылок Node.NodeList возвращает копию соответствующих дочерних узлов. Из-за этого мы не можем использовать метод java.util.NodeList#removeAll непосредственно в нашей коллекции статей .

Чтобы удалить узел по предикату, мы должны сначала найти все узлы, соответствующие нашему условию, а затем пройтись по ним и каждый раз вызывать метод java.util.Node#remove для родителя .

Давайте реализуем тест, удаляющий все статьи, id автора которых отличается от 3 :

 def "Should remove article from xml"()    given: "XML object"   def articles = new XmlParser().parse(xmlFile)    when: "Removing all articles but the ones with >  articles.article  .findAll  it.author.'@id'.text() != "3" >   .each  articles.remove(it) >    then: "There is only one article left"   articles.children().size() == 1   articles.article[0].author.'@id'.text() == "3"   > 

Как мы видим, в результате нашей операции удаления мы получили XML-структуру только с одной статьей, а ее id равен 3 .

4. XmlSlurper

Groovy также предоставляет еще один класс, предназначенный для работы с XML. В этом разделе мы покажем, как читать XML-структуру и управлять ею с помощью XmlSlurper.

4.1. Чтение

Как и в наших предыдущих примерах, начнем с разбора структуры XML из файла:

 def "Should read XML file properly"()    given: "XML file"    when: "Using XmlSlurper to read file"   def articles = new XmlSlurper().parse(xmlFile)    then: "Xml is loaded properly"   articles.'*'.size() == 4   articles.article[0].author.firstname == "Siena"   articles.article[2].'release-date' == "2018-06-12"   articles.article[3].title == "Java 12 insights"   articles.article.find  it.author.'@id' == "3" >.author.firstname == "Daniele"   > 

Как мы видим, интерфейс идентичен интерфейсу XmlParser . Однако структура вывода использует groovy.util.slurpersupport.GPathResult , который является классом-оболочкой для Node . GPathResult предоставляет упрощенные определения таких методов, как equals() и toString() , обертывая Node#text(). В результате мы можем читать поля и параметры напрямую, используя только их имена.

4.2. Добавление узла

Добавление узла также очень похоже на использование XmlParser . Однако в этом случае groovy.util.slurpersupport. GPathResult#appendNode предоставляет метод, который принимает экземпляр java.lang.Object в качестве аргумента. В результате мы можем упростить новые определения узлов , следуя тому же соглашению, введенному Node Builder :

 def "Should add node to existing xml"()    given: "XML object"   def articles = new XmlSlurper().parse(xmlFile)    when: "Adding node to xml"   articles.appendNode    article(id: '5')    title('Traversing XML in the nutshell')   author    firstname('Martin')   lastname('Schmidt')   >   'release-date'('2019-05-18')   >   >    articles = new XmlSlurper().parseText(XmlUtil.serialize(articles))    then: "Node is added to xml properly"   articles.'*'.size() == 5   articles.article[4].title == "Traversing XML in the nutshell"   > 

Если нам нужно изменить структуру нашего XML с помощью XmlSlurper, мы должны повторно инициализировать наш объект article, чтобы увидеть результаты. Мы можем добиться этого, используя комбинацию методов groovy.util.XmlSlurper #parseText и groovy.xmlXmlUtil#serialize .

4.3. Изменение узла

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

 def "Should modify node"()    given: "XML object"   def articles = new XmlSlurper().parse(xmlFile)    when: "Changing value of one of the nodes"   articles.article.each  it.'release-date' = "2019-05-18" >    then: "XML is updated"   articles.article.findAll  it.'release-date' != "2019-05-18" >.isEmpty()   > 

Заметим, что когда мы изменяем только значения объекта XML, нам не нужно снова анализировать всю структуру.

4.4. Замена узла

Теперь приступим к замене всего узла. И снова на помощь приходит GPathResult . Мы можем легко заменить узел, используя groovy.util.slurpersupport.NodeChild#replaceNode , который расширяет GPathResult и следует тому же соглашению об использовании значений Object в качестве аргументов:

 def "Should replace node"()    given: "XML object"   def articles = new XmlSlurper().parse(xmlFile)    when: "Replacing node"   articles.article[0].replaceNode    article(id: '5')    title('Traversing XML in the nutshell')   author    firstname('Martin')   lastname('Schmidt')   >   'release-date'('2019-05-18')   >   >    articles = new XmlSlurper().parseText(XmlUtil.serialize(articles))    then: "Node is replaced properly"   articles.'*'.size() == 4   articles.article[0].title == "Traversing XML in the nutshell"   > 

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

4.5. Удаление узла

Чтобы удалить узел с помощью XmlSlurper, мы можем повторно использовать метод groovy.util.slurpersupport.NodeChild#replaceNode , просто указав пустое определение узла :

 def "Should remove article from xml"()    given: "XML object"   def articles = new XmlSlurper().parse(xmlFile)    when: "Removing all articles but the ones with >  articles.article  .findAll  it.author.'@id' != "3" >   .replaceNode >    articles = new XmlSlurper().parseText(XmlUtil.serialize(articles))    then: "There is only one article left"   articles.children().size() == 1   articles.article[0].author.'@id' == "3"   > 

Опять же, изменение структуры XML требует повторной инициализации нашего объекта article .

5. XmlParser против XmlSlurper

Как мы показали в наших примерах, использование XmlParser и XmlSlurper очень похоже. Мы можем более или менее достичь одинаковых результатов с обоими. Однако некоторые различия между ними могут склонить чашу весов в сторону одного или другого.

Во-первых, XmlParser всегда анализирует весь документ в виде DOM-структуры. Благодаря этому мы можем одновременно читать и писать в него . Мы не можем сделать то же самое с XmlSlurper , так как он оценивает пути более лениво. В результате XmlParser может потреблять больше памяти. «

С другой стороны, XmlSlurper использует более простые определения, что упрощает работу с ним. Мы также должны помнить, что любые структурные изменения, внесенные в XML с помощью XmlSlurper , требуют повторной инициализации, что может привести к неприемлемому снижению производительности в случае внесения множества изменений друг за другом.

Решение о том, какой инструмент использовать, должно приниматься с осторожностью и полностью зависит от варианта использования.

6. Разметчик

Помимо чтения XML-дерева и управления им, Groovy также предоставляет инструменты для создания XML-документа с нуля. Давайте теперь создадим документ, состоящий из первых двух статей из нашего первого примера, используя groovy.xml.MarkupBuilder :

 def "Should create XML properly"()    given: "Node structures"    when: "Using MarkupBuilderTest to create xml structure"   def writer = new StringWriter()   new MarkupBuilder(writer).articles    article    title('First steps in Java')   author(id: '1')    firstname('Siena')   lastname('Kerr')   >   'release-date'('2018-12-01')   >   article    title('Dockerize your SpringBoot application')   author(id: '2')    firstname('Jonas')   lastname('Lugo')   >   'release-date'('2018-12-01')   >   >    then: "Xml is created properly"   XmlUtil.serialize(writer.toString()) == XmlUtil.serialize(xmlFile.text)   > 

В приведенном выше примере мы видим, что MarkupBuilder использует тот же подход для определений узлов , который мы использовали ранее с NodeBuilder и GPathResult .

Чтобы сравнить выходные данные MarkupBuilder с ожидаемой структурой XML, мы использовали метод groovy.xml.XmlUtil#serialize .

7. Заключение

В этой статье мы рассмотрели несколько способов управления XML-структурами с помощью Groovy.

Мы рассмотрели примеры разбора, добавления, редактирования, замены и удаления узлов с помощью двух классов, предоставляемых Groovy: XmlParser и XmlSlurper . Мы также обсудили различия между ними и показали, как можно с нуля построить дерево XML с помощью MarkupBuilder .

Как всегда, полный код, использованный в этой статье, доступен на GitHub .

Вызов JavaScript-кода из JavaFX-апплета

Вызов JavaScript-кода из JavaFX-апплета обеспечивает метод getHostServices() класса javafx.application.Application , возвращающий объект javafx.application. HostServices , который позволяет взаимодействовать с Web-страницей, содержащей JavaFX-апплет. Для взаимодействия с Web-страницей JavaFX-апплета класс HostServices предос- тавляет методы getCodeBase() , getDocumentBase() , resolveURI() , showDocument() и getWebContext() . Метод getWebContext() возвращает объект netscape.javascript.JSObject , обеспечи- вающий доступ к JavaScript-коду с помощью методов:  public Object getMember(String name) — эквивалент this.name языка JavaScript;  public Object getSlot(int index) — эквивалент this[index] языка JavaScript;

272 Глава 7

 public void setMember(String name, Object value) — эквивалент this.name = value языка JavaScript;  public void setSlot(int index, Object value) — эквивалент this[index] = value языка JavaScript;  public void removeMember(String name) — удаляет именованный член;  public Object call(String methodName, Object args[]) — эквивалент this.methodName (args[0], args[1], . ) языка JavaScript;  public Object eval(String s) — вычисляет JavaScript-выражение. В качестве примера использования JavaScript-кода в Java-коде дополним проект JavaFXApplicationLiveConnect Web-страницей, из которой можно будет загрузить Web-страницу, содержащую набор ссылок на изображения и JavaFX-апплет, демонстрирующий изображения. Для этого откроем среду NetBeans с поддержкой платформы JavaFX 2.0 и в меню Файл выберем Создать проект | JavaFX | JavaFX Application , нажмем кнопку Далее и введем имя проекта JavaFXApplicationJSObject, отметим флажок Create Main Class , введем имя класса javafxapplication.JavaFXApplication и нажмем кнопку Готово . В окне редактора среды NetBeans дополним код сгенерированного класса JavaFXApplication согласно листингу 7.3. Листинг 7.3. Код класса JavaFXApplication проекта JavaFXApplicationJSObject package javafxapplication; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Cursor; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.paint.Color; import javafx.stage.Stage; import netscape.javascript.JSObject; public class JavaFXApplication extends Application < public static void main(String[] args) < Application.launch(args); >@Override public void start(Stage primaryStage) < primaryStage.setTitle("Hello World"); Group root = new Group(); Scene scene = new Scene(root, 300, 200, Color.BEIGE); Button btn = new Button(); btn.setLayoutX(35); btn.setLayoutY(80); btn.setText("Открыть изображения"); btn.setCursor(Cursor.CLOSED_HAND);

Совместное использование JavaScript и JavaFX 273

btn.setStyle(«-fx-font: bold 16pt Georgia;-fx-text-fill: white; -fx-background-color: #a0522d;-fx-border-width: 3px; -fx-border-color:#f4a460 #800000 #800000 #f4a460;» ); btn.setOnAction(new EventHandler() < @Override public void handle(ActionEvent e) < JSObject js = getHostServices().getWebContext(); String baseURI=getHostServices().getCodeBase(); if (js != null) < try < js.eval("window.location.replace ('"+baseURI+"JavaFXApplicationLiveConnect.html');"); >catch (Exception ex) < >>> >); root.getChildren().add(btn); primaryStage.setScene(scene); primaryStage.show(); >> В методе start() класса JavaFXApplication создается кнопка Button с обработчиком событий, в котором с помощью метода getCodeBase() класса HostServices получается URI-адрес каталога, содержащего JAR-файл JavaFX-приложения JavaFXApplicationJSObject и Web-страницу JavaFX-приложения JavaFXApplicationLiveConnect. Далее на основе полученного базового URI-адреса и с помощью объекта JSObject текущая Web-страница заменяется Web-страницей JavaFX-приложения JavaFXApplicationLiveConnect. Выбрав опцию Очистить и построить для проекта JavaFXApplicationJSObject, скопируем файлы папки dist проекта JavaFXApplicationJSObject в папку dist проек- та JavaFXApplicationLiveConnect. Теперь, открыв Web-страницу JavaFXApplicationJSObject.html в Web-браузере, можно будет увидеть кнопку Открыть изображения , при нажатии которой загру- зится Web-страница JavaFXApplicationLiveConnect.html. П РИМЕЧАНИЕ Проекты JavaFXApplicationJSObject и JavaFXApplicationLiveConnect с примером совместно- го использования JavaScript и JavaFX находятся в папке Примеры\Глава7 в электронном архиве (см. приложение в конце книги).

Использование JavaScript в WebView

Для JavaFX-компонента WebView прямое использование JavaScript-кода обеспечива- ет метод executeScript(java.lang.String script) класса javafx.scene.web.WebEngine , принимающий в качестве аргумента строку JavaScript-кода и исполняющий его в контексте текущей Web-страницы.

274 Глава 7

В качестве примера использования JavaScript-кода узлом WebView модифицируем проект JavaFXApplicationWebView из разд. «Отображение Web-контента» главы 2 . Дополним код метода start() класса JavaFXApplication проекта JavaFXApplicationWebView: private Double zoom=1.0; Button btnP = new Button(); btnP.setText(«+»); btnP.setCursor(Cursor.CLOSED_HAND); btnP.setStyle(«-fx-font: bold 16pt Georgia;-fx-text-fill: white;-fx-background- color: #a0522d;-fx-border-width: 3px; -fx-border-color:#f4a460 #800000 #800000 #f4a460;» ); btnP.setOnAction(new EventHandler() < @Override public void handle(ActionEvent e) < zoom=zoom*1.1; webView.getEngine().executeScript("document.body.style.zoom p1339 ft50">Button btnM = new Button(); btnM.setText(«-«); btnM.setCursor(Cursor.CLOSED_HAND); btnM.setStyle(«-fx-font: bold 16pt Georgia;-fx-text-fill: white;-fx-background- color: #a0522d;-fx-border-width: 3px; -fx-border-color:#f4a460 #800000 #800000 #f4a460;» ); btnM.setOnAction(new EventHandler() < @Override public void handle(ActionEvent e) < zoom=zoom/1.2; webView.getEngine().executeScript("document.body.style.zoom p102 ft48">>>); hbox.getChildren().addAll(textField, btn, btnP,btnM); В граф сцены JavaFX-приложения с компонентом WebView добавлены две кнопки, при нажатии которых Web-страница, отображаемая узлом WebView , увеличивается или уменьшается. Достигается этот эффект с помощью изменения свойств объекта BODY методом executeScript() класса WebEngine . После выбора пункта Очистить и построить в окне Проекты среды NetBeans для измененного проекта JavaFX-приложения и пункта Выполнить можно будет увидеть окно JavaFX-приложения с панелью инструментов, содержащей три кнопки и текстовое поле, после ввода в которое URL-адреса Web-страницы узел WebView отобразит ее, а при нажатии на кнопки + и – отображаемая Web-страница будет увеличиваться и уменьшаться (рис. 7.2). П РИМЕЧАНИЕ Проект JavaFXApplicationWebViewWithJavaScript с примером совместного использования JavaScript и WebView находится в папке Примеры\Глава7 в электронном архиве (см. прило- жение в конце книги) .

Совместное использование JavaScript и JavaFX 275

Рис. 7.2. JavaFX-приложение с узлом WebView и использованием JavaScript-кода

276 Глава 7

Г Л АВ А 8 Выполнение фоновых задач Для выполнения определенной работы, занимающей значительное время процессора, в отдельном потоке от потока JavaFX Application Thread, для того чтобы не блокировать GUI-интерфейс JavaFX-приложения, платформа JavaFX 2.0 предлагает использовать пакет javafx.concurrent. Работу с фоновыми потоками пакет javafx.concurrent обеспечивает с помощью двух абстрактных классов — Task и Service , реализующих интерфейс javafx. concurrent.Worker . Абстрактный класс Task расширяет класс java.util.concurrent.FutureTask , и конкретные реализации абстрактного класса Task должны переопределять его метод call() , вызываемый средой выполнения в фоновом потоке. Результатом выполнения задачи Task в фоновом потоке является объект V , возвращаемый методом call() . Результат выполнения фоновой задачи может быть получен с помощью ме- тода getValue() класса Task . В методе call() разрешено вызывать только методы updateProgress() , updateMessage() и updateTitle() класса Task , обновляющие текущее количественное выполнение задачи, максимальное количественное выполнение задачи, текущее процентное выполнение задачи, сообщение, связанное с текущим состоянием выполнения задачи, и заголовок данной задачи. В то время как метод call() выполняется в фоновом потоке, методы updateProgress() , updateMessage() и updateTitle() класса Task выполняются в по- токе JavaFX Application Thread. Поэтому, переопределяя данные методы в конкретной реализации абстрактного класса Task и обеспечивая их вызов в методе call() , можно передавать значения свойств workDone , totalWork , progress , message и title из фонового потока в поток JavaFX Application Thread. Запуск экземпляра пользовательского класса, расширяющего класс Task , и соответственно вызов его метода call() может быть выполнен различными способами. Так как класс Task расширяет класс FutureTask , то запуск его экземпляра может быть осуществлен с помощью объекта java.lang.Thread : new Thread(task).start();

278 Глава 8 Запуск объекта Task также может быть произведен посредством программного интерфейса Java Executor API. В этом случае необходимо создать объект java.util.concurrent.ExecutorService с помощью класса-фабрики java.util. concurrent.Executors и воспользоваться методом submit() интерфейса ExecutorService для запуска объекта Task . Для завершения работы объекта ExecutorService нужно применить метод shutdown() интерфейса ExecutorService . Помимо перечисленных способов, создание и запуск объекта Task обеспечивает сервисный класс Service пакета javafx.concurrent. Абстрактный класс Service также необходимо расширять конкретной реализацией с переопределением его метода createTask() , возвращающего объект Task . Запуск объекта Service осуществляется с помощью метода start() , перезапуск объекта Service — с помощью метода restart() , а сброс объекта Service — с помощью метода reset() . Прерывание выполнения фоновой задачи обеспечивает метод cancel() класса Service . Получить результат выполнения фоновой задачи можно с помощью метода getValue() класса Service . В качестве примера выполнения фоновой задачи в JavaFX-приложении рассмотрим создание программы, в которой индикатор ProgressBar отображает ход вычислений, производимых в фоновом потоке. Для создания такого приложения откроем среду NetBeans с поддержкой платформы JavaFX 2.0 и в меню Файл выберем Создать проект | JavaFX | JavaFX Application , нажмем кнопку Далее и введем имя проекта JavaFXApplicationTask, отметим флажок Create Main Class , введем имя класса javafxapplication.JavaFXApplication и нажмем кнопку Готово . В окне редактора среды NetBeans дополним код сгенерированного класса JavaFXApplication , который расширяет класс javafx.application.Application и имеет два метода — main() и start() . П РИМЕЧАНИЕ Проект JavaFXApplicationTask с примером выполнения фоновой задачи в JavaFXприложении находится в папке Примеры\Глава8 в электронном архиве (см. приложение в конце книги) . В методе main() класса JavaFXApplication вызывается функция загрузки JavaFXприложения: Application.launch(JavaFXApplication.class, args); В методе start() класса JavaFXApplication производится обработка объекта Stage , передаваемого методу start() в качестве аргумента, с созданием и отображением графа сцены: 1. Устанавливается заголовок основного окна JavaFX-приложения: primaryStage.setTitle(«Тестирование GUI-компонентов»); 2. Создаются корневой узел графа сцены и на его основе экземпляр сцены, который устанавливается для объекта Stage , и объект Stage становится видимым: Group root = new Group(); Scene scene = new Scene(root, 500, 500, Color.LIGHTGREEN);

Выполнение фоновых задач 279

primaryStage.setScene(scene); primaryStage.show(); 3. Создается экземпляр индикатора ProgressBar , который помещается в левый верхний угол основного окна JavaFX-приложения и для которого определяются такие свойства, как курсор мыши, визуальный эффект, всплывающая подсказка, предпочтительные размеры и начальное значение свойства progress : final ProgressBar pb=new ProgressBar(); pb.setLayoutX(20); pb.setLayoutY(50); pb.setCursor(Cursor.TEXT); DropShadow effect=new DropShadow(); effect.setOffsetX(8); effect.setOffsetY(8); pb.setEffect(effect); pb.setTooltip(new Tooltip(«Индикатор выполнения задачи»)); pb.setPrefSize(200,30); pb.setProgress(0.0); 4. Создаются две Button -кнопки — Start и Reset , которые размещаются под индикатором ProgressBar . Для кнопки Start определяется обработчик событий, в котором создается экземпляр анонимного класса, расширяющего класс Task с переопределением методов call() и updateProgress() . В методе call() имитируются продолжительные вычисления и вызывается метод updateProgress() с передачей ему в качестве аргумента текущего значения выполнения вычислений. В методе updateProgress() на основе текущего значения выполнения вычислений устанавливается прогресс индикатора ProgressBar . Также в обработчике событий кнопки Start демонстрируются три способа запуска выполнения фоновой задачи с помощью объектов Thread , ExecutorService и Service . Для кнопки Reset определяется обработчик событий, в котором индикатор ProgressBar устанавливается в начальное состояние: Button btnS=new Button(«Start»); btnS.setLayoutX(20); btnS.setLayoutY(100); btnS.setStyle(«-fx-font: 16pt Arial;»); btnS.setOnAction(new EventHandler() < @Override public void handle(ActionEvent e) < final Task task = new Task() < @Override protected Void call() throws Exception < int max = 10; for (int i = 1; i

280 Глава 8

return null; > @Override protected void updateProgress(long workDone, long max)< pb.setProgress((double)workDone/(double)max); >>; //new Thread(task).start(); ExecutorService es = Executors.newSingleThreadExecutor (); // es.submit (task); // es.shutdown (); Service service=new Service() < @Override protected Task createTask() < return task; >>; service.start(); >>); Button btnR=new Button(«Reset»); btnR.setLayoutX(100); btnR.setLayoutY(100); btnR.setStyle(«-fx-font: 16pt Arial;»); btnR.setOnAction(new EventHandler() < @Override public void handle(ActionEvent e) < pb.setProgress(0.0); >>); 5. И в заключение индикатор ProgressBar и кнопки Button добавляются в корневой узел графа сцены: root.getChildren().add(pb); root.getChildren().add(btnS); root.getChildren().add(btnR); Запустить созданное JavaFX-приложение можно, щелкнув в окне Проекты среды NetBeans правой кнопкой мыши на узле проекта JavaFXApplicationTask и выбрав пункт Очистить и построить , а затем — Выполнить . В результате можно будет увидеть индикатор и две кнопки — Start и Reset . При нажатии кнопки Start изменяется прогресс индикатора, а при нажатии кнопки Reset индикатор устанавливается в исходное состояние.

Г Л АВ А 9 Совместное использование Swing и JavaFX Пакет javafx.embed.swing платформы JavaFX 2.0 обеспечивает встраивание JavaFXсцены в Swing-приложение. Для этого пакет javafx.embed.swing предоставляет ком- понент JFXPanel . Класс JFXPanel расширяет класс javax.swing.JComponent , являющийся базовым классом для всех Swing-компонентов, помещаемых в Swing-контейнеры верхнего уров- ня JFrame , JDialog и JApplet . Класс JFXPanel имеет, помимо унаследованных от класса JComponent методов, пуб- личные методы getScene() , setScene() , setOpaque() , isOpaque() , getPreferredSize() , addNotify() и removeNotify() . Экземпляр класса JFXPanel может быть создан с помощью конструктора public JFXPanel() или посредством класса-фабрики javafx.embed.swing.JFXPanelBuilder . При использовании компонента JFXPanel в Swing-приложении необходимо учитывать, что Swing-компоненты обрабатываются в потоке Event Dispatch Thread, а JavaFX-компоненты — в потоке JavaFX Application Thread. Поэтому для инициализации и изменения графа JavaFX-сцены в Swing-приложении используется шаблон программирования: Platform.runLater(new Runnable() < @Override public void run() < doSomethingWithJavaFX(); >>); А для инициализации и изменения Swing-интерфейса применяется шаблон программирования: SwingUtilities.invokeLater(new Runnable() < @Override public void run() < doSomethingWithSwing(); >>);

282 Глава 9

В качестве примера использования компонента JFXPanel в Swing-приложении рассмотрим встраивание узла MediaView , воспроизводящего медиаконтент. При этом основой будет служить проект JavaFXApplicationMediaView из разд. «Воспроиз- ведение аудио и видео» главы 2 . Для создания Swing-приложения откроем среду NetBeans и в меню Файл выберем Создать проект | Java | Java Application , введем имя проекта JavaApplicationSwing, сбросим флажок Создать главный класс и нажмем кнопку Готово . В окне Проекты среды NetBeans щелкнем правой кнопкой мыши на узле проекта JavaApplicationSwing и выберем Создать | Другое | Формы Swing GUI | Форма Примера Приложения , нажмем кнопку Далее , введем имя класса MainFrame и имя пакета main и нажмем кнопку Готово . В результате средой NetBeans будет сгенерирован код класса MainFrame , который расширяет класс javax.swing.JFrame , имеет метод main() — точку входа в Swingприложение и представляет главное окно приложения с панелью меню File , Edit и Help . В окне Проекты среды NetBeans выбором Добавить архив JAR или папку для узла Библиотеки проекта JavaApplicationSwing добавим в путь приложения биб- лиотеку jfxrt.jar платформы JavaFX 2.0. В представлении Design среды NetBeans, в окне [JFrame]-свойства , изменим размеры preferredSize главного окна приложения на [600, 600] . В представлении Source среды NetBeans дополним код класса MainFrame методом initFX() (лис- тинг 9.1). Листинг 9.1. Код метода initFX() класса MainFrame private void initFX(javafx.embed.swing.JFXPanel fxPanel) < // This method is invoked on JavaFX thread final Group root = new Group(); Scene scene = new Scene(root, 400, 400, Color.BLACK); fxPanel.setScene(scene); Media video = new Media(this.getClass(). getResource("video.flv").toString()); final MediaPlayer playerVideo=new MediaPlayer(video); final MediaView media=new MediaView(playerVideo); media.setFitHeight(300); media.setFitWidth(300); media.setPreserveRatio(true); Media audio = new Media(this.getClass().getResource("audio.mp3").toString()); final MediaPlayer playerAudio=new MediaPlayer(audio); playerAudio.setCycleCount(MediaPlayer.INDEFINITE); Button btnPlay=new Button(); btnPlay.setTooltip(new Tooltip("Play"));

Совместное использование Swing и JavaFX 283

Image imPlay=new Image(this.getClass(). getResource(«player_play.png»).toString()); ImageView imvPlay=new ImageView(imPlay); imvPlay.setFitHeight(20); imvPlay.setFitWidth(20); btnPlay.setGraphic(imvPlay); btnPlay.setOnAction(new EventHandler() < @Override public void handle(ActionEvent e) < playerVideo.play(); playerAudio.play(); >>); Button btnPause=new Button(); btnPause.setTooltip(new Tooltip(«Pause»)); Image imPause=new Image(this.getClass(). getResource(«player_pause.png»).toString()); ImageView imvPause=new ImageView(imPause); imvPause.setFitHeight(20); imvPause.setFitWidth(20); btnPause.setGraphic(imvPause); btnPause.setOnAction(new EventHandler() < @Override public void handle(ActionEvent e) < playerVideo.pause(); playerAudio.pause(); >>); Button btnStop=new Button(); btnStop.setTooltip(new Tooltip(«Stop»)); Image imStop=new Image(this.getClass(). getResource(«player_stop.png»).toString()); ImageView imvStop=new ImageView(imStop); imvStop.setFitHeight(20); imvStop.setFitWidth(20); btnStop.setGraphic(imvStop); btnStop.setOnAction(new EventHandler() < @Override public void handle(ActionEvent e) < playerVideo.stop(); playerAudio.stop(); >>); final Slider sliderT=new Slider(); sliderT.setCursor(Cursor.CROSSHAIR); sliderT.setMax(31200); sliderT.setMin(0); sliderT.setOrientation(Orientation.HORIZONTAL); sliderT.setShowTickLabels(false); sliderT.setShowTickMarks(false);

284 Глава 9

sliderT.setOnMousePressed(new EventHandler() < @Override public void handle(MouseEvent event) < playerVideo.pause(); playerAudio.pause(); >>); sliderT.setOnMouseReleased(new EventHandler() < @Override public void handle(MouseEvent event) < playerVideo.seek(new Duration(sliderT.getValue())); >>); playerVideo.currentTimeProperty().addListener(new ChangeListener() < @Override public void changed(ObservableValueov, Duration old_value, Duration new_value) < if(new_value.toMillis()>31200) < playerVideo.stop(); playerAudio.stop(); playerVideo.play(); playerAudio.play(); >sliderT.setValue(new_value.toMillis()); >>); final Slider sliderV=new Slider(); sliderV.setCursor(Cursor.CROSSHAIR); sliderV.setTooltip(new Tooltip(«Volume»)); sliderV.setMaxWidth(70); sliderV.setMax(1.0); sliderV.setMin(0.0); sliderV.setValue(0.5); sliderV.setOrientation(Orientation.HORIZONTAL); sliderV.setShowTickLabels(false); sliderV.setShowTickMarks(false); sliderV.valueProperty().addListener(new ChangeListener() < @Override public void changed(ObservableValueov, Number old_val, Number new_val) < playerAudio.setVolume(sliderV.getValue()); >>); Label volume=new Label(); Image imVolume=new Image(this.getClass(). getResource(«volume.jpeg»).toString()); ImageView imvVolume=new ImageView(imVolume); imvVolume.setFitHeight(20); imvVolume.setFitWidth(20); volume.setGraphic(imvVolume);

Совместное использование Swing и JavaFX 285

HBox hbox=new HBox(); hbox.setSpacing(5); hbox.setAlignment(Pos.CENTER); hbox.getChildren().addAll(btnPlay,btnPause,btnStop,sliderV,volume); VBox vbox=new VBox(); vbox.setSpacing(20); vbox.setAlignment(Pos.CENTER); VBox.setMargin(sliderT, new Insets(0,30,0,30)); HBox.setMargin(sliderV, new Insets(0,0,0,30)); vbox.getChildren().addAll(media,sliderT,hbox); StackPane pane=new StackPane(); pane.setAlignment(Pos.CENTER); pane.setCursor(Cursor.HAND); pane.setLayoutX(20); pane.setLayoutY(20); Rectangle rec=new Rectangle(350,350); rec.setFill(Color.BLACK); rec.setStroke(Color.NAVY); rec.setStrokeWidth(5); rec.setStrokeType(StrokeType.OUTSIDE); rec.setArcHeight(10); rec.setArcWidth(20); pane.getChildren().addAll(rec,vbox); root.getChildren().addAll(pane); > Код метода initFX() класса MainFrame основывается на коде метода start() класса JavaFXApplication проекта JavaFXApplicationMediaView из разд. «Воспроизведение аудио и видео» главы 2 . Также изменим метод main() класса MainFrame (листинг 9.2). Листинг 9.2. Код метода main() класса MainFrame public static void main(String args[]) < final MainFrame mainFrame=new MainFrame(); final javafx.embed.swing.JFXPanel fxPanel = new javafx.embed.swing.JFXPanel(); mainFrame.add(fxPanel,null); fxPanel.setBounds(50,30,400, 400); javafx.application.Platform.runLater(new Runnable() < @Override public void run() < mainFrame.initFX(fxPanel); >>); java.awt.EventQueue.invokeLater(new Runnable() < @Override

286 Глава 9

public void run() < mainFrame.setVisible(true); >>); > В коде метода main() класса MainFrame создается экземпляр класса MainFrame и экземпляр компонента JFXPanel , который добавляется в JFrame -окно MainFrame . При этом сцена компонента JFXPanel формируется в потоке JavaFX Application Thread с помощью метода initFX() , а JFrame -окно MainFrame становится видимым в потоке Event Dispatch Thread. После выбора в окне Проекты среды NetBeans пункта Очистить и построить для проекта JavaApplicationSwing и затем пункта Выполнить можно будет увидеть окно Swing-приложения со встроенным видеоплеером (рис. 9.1). Рис. 9.1. Swing-приложение со встроенным JavaFX-плеером

Для развертывания созданного Swing-приложения в качестве JWS-приложения с подключением среды выполнения платформы JavaFX 2.0 в окне Проекты среды NetBeans выберем опцию Свойства для проекта JavaApplicationSwing. Затем в по-

Совместное использование Swing и JavaFX 287

явившемся диалоговом окне выберем категорию Web Start , отметим флажки Включить Web Start и Разрешить выполнение в автономном режиме и нажмем кнопку OK . После выбора в окне Проекты среды NetBeans пункта Очистить и построить для проекта JavaApplicationSwing необходимо дополнить в редакторе кода JNLP-файл launch.jnlp из папки dist каталога проекта (листинг 9.3). Листинг 9.3. Код JNLP-файла проекта JavaApplicationSwing JavaApplicationSwing тимур JavaApplicationSwing JavaApplicationSwing javafx/javafx-windows-i586__Vlatest.exe»/> javafx/javafx-windows-x64__Vlatest.exe»/> Теперь созданное Swing-приложение будет разворачиваться как JWS-приложение с подключением среды выполнения платформы JavaFX 2.0. П РИМЕЧАНИЕ

Проект JavaApplicationSwing с примером Swing-приложения, содержащего JavaFXкомпоненты, находится в папке Примеры\Глава9 в электронном архиве (см. приложение в конце книги).

288 Глава 9

Для встраивания узла MediaView в апплет javax.swing.JApplet откроем среду NetBeans и в меню Файл выберем Создать проект | Java | Java Application , вве- дем имя проекта JavaApplicationSwingApplet, сбросим флажок Создать главный класс и нажмем кнопку Готово . В окне Проекты среды NetBeans щелкнем правой кнопкой мыши на узле проекта JavaApplicationSwingApplet и выберем Создать | Другое | Формы Swing GUI | Форма JApplet , нажмем кнопку Далее , введем имя класса апплета JAppletWithJavaFX и имя пакета main и нажмем кнопку Готово . В результате средой NetBeans будет сгенерирован код класса JAppletWithJavaFX , расширяющего класс javax.swing.JApplet . В окне Проекты среды NetBeans выбором Добавить архив JAR или папку для узла Библиотеки проекта JavaApplicationSwingApplet добавим в путь приложения библиотеку jfxrt.jar платформы JavaFX 2.0. В представлении Design среды NetBeans, в окне [JApplet]-свойства , изменим размеры preferredSize окна апплета на [600, 600] . В представлении Source среды NetBeans дополним код класса JAppletWithJavaFX методом initFX() (см. листинг 9.1). Также изменим метод init() класса JAppletWithJavaFX (листинга 9.4). Листинг 9.4. Код метода init() класса JAppletWithJavaFX @Override public void init() < try < for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) < if ("Nimbus".equals(info.getName())) < javax.swing.UIManager.setLookAndFeel(info.getClassName()); break; >> > catch (ClassNotFoundException ex) < java.util.logging.Logger.getLogger(SwingJApplet.class.getName()). log(java.util.logging.Level.SEVERE, null, ex); >catch (InstantiationException ex) < java.util.logging.Logger.getLogger(SwingJApplet.class.getName()). log(java.util.logging.Level.SEVERE, null, ex); >catch (IllegalAccessException ex) < java.util.logging.Logger.getLogger(SwingJApplet.class.getName()). log(java.util.logging.Level.SEVERE, null, ex); >catch (javax.swing.UnsupportedLookAndFeelException ex) < java.util.logging.Logger.getLogger(SwingJApplet.class.getName()). log(java.util.logging.Level.SEVERE, null, ex); final javafx.embed.swing.JFXPanel fxPanel = new javafx.embed.swing.JFXPanel();

Совместное использование Swing и JavaFX 289

add(fxPanel,null); fxPanel.setBounds(50,30,400, 400); javafx.application.Platform.runLater(new Runnable() < @Override public void run() < initFX(fxPanel); >>); try < java.awt.EventQueue.invokeAndWait(new Runnable() < public void run() < initComponents(); >>); > catch (Exception ex) < ex.printStackTrace(); >> В коде метода init() класса JAppletWithJavaFX создается экземпляр компонента JFXPanel , который добавляется в апплет JAppletWithJavaFX. При этом сцена компонента JFXPanel формируется в потоке JavaFX Application Thread с помощью метода initFX() , а инициализация Swing-компонентов апплета JAppletWithJavaFX произво- дится в потоке Event Dispatch Thread. Для развертывания созданного JApplet-апплета с помощью JWS с подключением среды выполнения платформы JavaFX 2.0, в окне Проекты среды NetBeans выберем пункт Свойства для проекта JavaApplicationSwingApplet. Затем в появившемся диалоговом окне выберем категорию Web Start , отметим флажки Включить Web Start и Разрешить выполнение в автономном режиме , выберем переключатель Дескриптор апплета , кнопкой Параметры апплета установим ширину и высоту апплета со значениями (600, 600) и нажмем кнопку OK . После выбора в окне Проекты среды NetBeans пункта Очистить и построить для проекта JavaApplicationSwingApplet необходимо дополнить в редакторе кода JNLPфайл launch.jnlp из папки dist каталога проекта (листинг 9.5). Листинг 9.5. Код JNLP-файла проекта JavaApplicationSwingApplet JavaApplicationSwingApplet тимур JavaApplicationSwingApplet JavaApplicationSwingApplet

290 Глава 9

Теперь созданный JApplet-апплет будет разворачиваться с помощью JWS с подключением среды выполнения платформы JavaFX 2.0. После открытия HTML-страницы папки dist каталога проекта в Web-браузере можно будет увидеть окно JApplet-апплета со встроенным видеоплеером. П РИМЕЧАНИЕ Проект JavaApplicationSwingApplet с примером JApplet-апплета, содержащего JavaFX- компоненты, находится в папке Примеры\Глава9 в электронном архиве (см. приложение в конце книги) .

Г Л АВ А 10 Компоненты JavaFX Beans и связывание данных Платформа Java определяет JavaBeans-компоненты, которые используются для передачи данных. JavaBeans-компоненты представлены Java-классами, созданными по определенным правилам. Классы JavaBeans-компонентов имеют публичный конструктор без параметров, свойства класса доступны через методы get() и set() , классы имеют методы add() и remove() добавления и удаления слушателей событий, а также классы обладают свойством сериализуемости (преобразования в последовательность байтов и обратно) путем реализации интерфейса java.io.Serializable . Кроме того, платформа Java содержит программный интерфейс JavaBeans API для создания и обработки JavaBeans-компонентов. Платформа JavaFX 2.0 расширяет JavaBeans-модель, определяя JavaFX-свойства. JavaFX-свойства технологии JavaFX 2.0 реализованы пакетами javafx.beans, javafx.beans.binding, javafx.beans.property и javafx.beans.value программного интерфейса JavaFX 2.0 API. Конечной реализацией JavaFX-свойств являются классы SimpleBooleanProperty , SimpleDoubleProperty , SimpleFloatProperty , SimpleIntegerProperty , SimpleLongProperty , SimpleObjectProperty и SimpleStringProperty пакета javafx.beans.property для свойств записи и чтения и классы ReadOnlyBooleanWrapper , ReadOnlyDoubleWrapper , ReadOnlyFloatWrapper , ReadOnlyIntegerWrapper , ReadOnlyLongWrapper , ReadOnlyObjectWrap- per и ReadOnlyStringWrapper пакета javafx.beans.property для свойств только чтения. Данные классы реализуют интерфейсы javafx.beans.Observable , javafx.beans. property.Property , javafx.beans.property.ReadOnlyProperty , javafx.beans.value. ObservableValue и javafx.beans.value.WritableValue , предоставляя методы:  addListener() и removeListener() — присоединения и удаления слушателей недействительности и изменения значения свойства;  bind() , bindBidirectional() , unbind() и unbindBidirectional() — связывания и уда- ления связывания значения свойства;  getBean() и getName() , возвращающие объект JavaFX Beans-компонента, содержащего свойство и имя свойства;  get() и set() — чтения и записи значения свойства.

292 Глава 10

Кроме того, данные классы расширяют классы XXXExpression пакета javafx.beans.binding, обеспечивающие для JavaFX-свойств методы создания объектов XXXBinding , представляющих выражения, результат которых синхронизирован со значением данного свойства и со значением объекта, выступающим в качестве аргумента вышеупомянутых методов. Таким образом, JavaFX Beans-компонент представлен классом, имеющим:  публичный конструктор без параметров;  методы get() и set() доступа к свойствам класса;  методы xxxProperty() , возвращающие JavaFX-свойства. В качестве примера рассмотрим класс javafx.scene.control.Button программного интерфейса JavaFX API. Класс Button имеет:  унаследованные от класса javafx.scene.Node свойства: blendMode , boundsInLocal , boundsInParent , cacheHint , cache , clip , cursor , depthTest , disabled , disable , effect , eventDispatcher , focused , focusTraversable , hover , id , inputMethodRequests , layoutBounds , layoutX , layoutY , managed , mouseTransparent , onDragDetected , onDragDone , onDragDropped , onDragEntered , onDragExited , onDragOver , onInputMethodTextChanged , onKeyPressed , onKeyReleased , onKeyTyped , onMouseClicked , onMouseDragged , onMouseEntered , onMouseExited , onMouseMoved , onMousePressed , onMouseReleased , opacity , parent , pickOnBounds , pressed , rotate , rotationAxis , scaleX , scaleY , scaleZ , scene , style , translateX , translateY , translateZ , visible ;  унаследованное от класса javafx.scene.Parent свойство needsLayout ;  унаследованные от класса javafx.scene.control.Control свойства: contextMenu , height , maxHeight , maxWidth , minHeight , minWidth , prefHeight , prefWidth , skin , tooltip , width ;  унаследованные от класса javafx.scene.control.Labeled свойства: alignment , contentDisplay , font , graphic , graphicTextGap , labelPadding , mnemonicParsing , textAlignment , textFill , textOverrun , text , underline , wrapText ;  унаследованные от класса javafx.scene.control.ButtonBase свойства armed и onAction ;  собственные свойства cancelButton и defaultButton . Какие-то из этих свойств доступны для чтения и записи, а какие-то — только для чтения. Для каждого свойства чтения и записи класс Button предоставляет методы доступа get() и set() , для свойства только чтения — метод get() , а также метод xxxProperty() , возвращающий JavaFX-свойство ХХХProperty или ReadOnlyХХХProperty . Рассмотрим свойство text класса Button . Метод textProperty() Button -объекта возвращает JavaFX-свойство javafx.beans. property.StringProperty , определяющее значение свойства text . Установить значение данного JavaFX-свойства и соответственно значение свойства text кнопки

Компоненты JavaFX Beans и связывание данных 293

Button можно методом set() интерфейса javafx.beans.value.WritableObjectValue или методом setValue() класса javafx.beans.property.StringProperty . Возвращает значение свойства text кнопки Button метод get() интерфейса javafx.beans.value. ObservableObjectValue или метод getValue() класса javafx.beans.binding. StringExpression : Button btn = new Button(); btn.textProperty().set(«Hello1»); System.out.println(btn.textProperty().get()); btn.textProperty().setValue(«Hello2»); System.out.println(btn.textProperty().getValue()); При этом методы getBean() и getName() вернут объект javafx.scene.control.Button и имя свойства text : System.out.println(btn.textProperty().getBean().getClass().getName()); //Вывод: javafx.scene.control.Button System.out.println(btn.textProperty().getName()); //Вывод: text Применяя метод bind() интерфейса javafx.beans.property.Property к JavaFX-свой- ству текста кнопки Button , можно связать его значение, например, со значением JavaFX-свойства текста поля javafx.scene.control.TextField так, что, набирая текст в поле TextField , он будет автоматически становиться текстом кнопки Button : TextField field=new TextField(); Button btn = new Button(); btn.textProperty().bind(field.textProperty()); Применяя метод bindBidirectional() класса javafx.beans.property.StringProperty к JavaFX-свойству текста кнопки Button , можно связать его значение, например, со значением JavaFX-свойства текста поля TextField так, что в нем будет автоматически отображаться измененный текст кнопки Button : TextField field=new TextField(); final Button btn = new Button(); btn.setOnAction(new EventHandler() < public void handle(ActionEvent event) < btn.textProperty().set("Hello"); >>); btn.textProperty().bindBidirectional(field.textProperty()); Организовать прослушивание изменения значения JavaFX-свойства текста кнопки Button позволяет метод addListener(ChangeListener listener) интерфейса javafx.beans.value.ObservableValue : TextField field1=new TextField(); final TextField field2=new TextField(); Button btn = new Button(); btn.textProperty().bind(field1.textProperty());

294 Глава 10

btn.textProperty().addListener(new ChangeListener() < @Override public void changed(ObservableValueo, String oldVal, String newVal)< field2.setText(newVal); >>); Так как JavaFX-свойства поддерживают отложенные вычисления своих значений, т. е. значение JavaFX-свойства пересчитывается не сразу после своего изменения, а только тогда, когда оно запрашивается методом get() , и в промежутке между изменением значения и его пересчетом генерируется событие недействительности зна- чения, метод addListener(InvalidationListener listener) интерфейса javafx.beans. Observable позволяет организовать прослушивание недействительности значения JavaFX-свойства текста кнопки Button : final Button btn = new Button(); btn.setOnAction(new EventHandler() < public void handle(ActionEvent event) < btn.textProperty().set("Hello"); >>); btn.textProperty().addListener(new InvalidationListener()< @Override public void invalidated(Observable observable) < System.out.println("Button text value invalidated"); >>); Здесь при нажатии кнопки Button возникнет событие недействительности значения JavaFX-свойства текста. Так как JavaFX-свойство StringProperty текста кнопки Button расширяет класс javafx.beans.binding.StringExpression , его методы позволяют организовать различ- ного рода связанные вычисления: TextField field1=new TextField(); TextField field2=new TextField(); Button btn = new Button(); btn.textProperty().set(«Hello «); StringBinding sb = (StringBinding)btn.textProperty().concat(field1.textProperty()); field2.textProperty().bind(sb); Здесь текст при вводе в поле field1 соединяется с текстом кнопки и результат отображается в поле field2 . В JavaFX-коде также можно использовать самостоятельные JavaFX-свойства, безотносительно к JavaFX-компонентам. Для этого необходимо создавать экземпляры классов SimpleBooleanProperty , SimpleDoubleProperty , SimpleFloatProperty , SimpleIntegerProperty , SimpleLongProperty , SimpleObjectProperty и SimpleStringProperty пакета javafx.beans.property . Так как данные классы расширяют классы XXXExpression пакета javafx.beans.binding, при этом можно создавать связанные вычисления. Для создания связанных вычислений платформа JavaFX 2.0 предлагает два типа программного интерфейса — High-Level Binding API и Low-Level Binding API.

Компоненты JavaFX Beans и связывание данных 295

Программный интерфейс High-Level Binding API также содержит Fluent API и предусматривает применение класса Bindings . Пример использования Fluent API: TextField field=new TextField(); SimpleDoubleProperty sp1=new SimpleDoubleProperty(); SimpleDoubleProperty sp2=new SimpleDoubleProperty(); NumberBinding sum=sp2.add(sp1); field.textProperty().bind(sum.asString()); sp1.set(4); sp2.set(3); //Вывод: 7.0 Здесь в поле TextField отобразится сумма значений двух JavaFX-свойств SimpleDoubleProperty . Пример использования класса Bindings : TextField field=new TextField(); SimpleDoubleProperty sp1=new SimpleDoubleProperty(); SimpleDoubleProperty sp2=new SimpleDoubleProperty(); NumberBinding sum=Bindings.add(sp1,sp2); field.textProperty().bind(sum.asString()); sp1.set(4); sp2.set(3); //Вывод: 7.0 Здесь в поле TextField также отобразится сумма значений двух JavaFX-свойств SimpleDoubleProperty . Пример использования Low-Level Binding API: TextField field=new TextField(); field.setPrefWidth(100); final SimpleDoubleProperty sp1=new SimpleDoubleProperty(); final SimpleDoubleProperty sp2=new SimpleDoubleProperty(); DoubleBinding db = new DoubleBinding() < < super.bind(sp1,sp2); >@Override protected double computeValue() < return (sp1.get()+sp2.get()); >>; NumberBinding sum=db; field.textProperty().bind(sum.asString()); sp1.set(4); sp2.set(3); //Вывод: 7.0 Для применения Low-Level Binding API необходимо расширять классы ХХХBinding пакета javafx.beans.binding с переопределением их метода computeValue() .

296 Глава 10

Г Л АВ А 11 Заставка запуска JavaFX-приложения От начала своего запуска до отображения главного окна JavaFX-приложение проходит четыре фазы процесса запуска и загрузки: 1. Инициализация среды выполнения. 2. Загрузка ресурсов приложения. 3. Инициализация приложения. 4. Выполнение кода приложения. Набор файлов папки dist проекта JavaFX-приложения обеспечивает заставки по умолчанию для первых двух фаз запуска и загрузки. Особенно это актуально для запуска JavaFX-апплета, встроенного в Web-страницу, и JWS-запуска настольного JavaFX-приложения, т. к. запуск отдельного JavaFX-приложения происходит достаточно быстро и не требует заставок. Улучшить отображение и обработку процесса запуска и загрузки JavaFXприложения можно несколькими способами. Первый способ — это использование обработчика onGetSplash программного интерфейса Deployment Toolkit API для создания окна запуска JavaFX-апплета, встроенного в Web-страницу. Другой способ — это применение CSS-стилей к Preloader-предзагрузчику по умолчанию. И наконец, можно создать свой класс предзагрузчика, расширяющий абстрактный класс javafx.application.Preloader . Обработчик onGetSplash возвращает HTML-код, отображаемый во время инициализации среды выполнения JavaFX-апплета. Для создания своей заставки запуска JavaFX-апплета откроем в редакторе кода HTML-страницу из папки dist проекта JavaFX-приложения, предварительно сгенерированную с помощью выбора пункта Очистить и построить среды NetBeans, и добавим следующий код:

298 Глава 11

var im=document.createElement(«img»); im.setAttribute(‘src’, ‘web-files/image.gif’); im.setAttribute(‘hspace’, ‘350’); im.setAttribute(‘vspace’, ‘100’); p.appendChild(im); return p; > function javafxEmbed_fxApp() < dtjava.embed( < id : 'fxApp', url : 'JavaFXApplicationWithSplash.jnlp', placeholder : 'javafx-app-placeholder', width : 800, height : 600, jnlp_content : '. . .' >, < javafx : '2.0+' >, < onGetSplash: getSplash >); > dtjava.addOnloadCallback(javafxEmbed_fxApp); Не забудем при этом добавить в папку web-files каталога dist проекта JavaFXприложения файл изображения image.gif. В результате при открытии Web-страницы JavaFX-приложения можно будет увидеть созданную заставку (рис. 11.1).

Рис. 11.1. JavaFX-приложение с пользовательской заставкой инициализации среды выполнения JavaFX-апплета

Заставка запуска JavaFX-приложения 299

П РИМЕЧАНИЕ Проект JavaFXApplicationWithSplash с примером создания заставки инициализации среды выполнения JavaFX-апплета находится в папке Примеры\Глава11 в электронном архиве (см. приложение в конце книги) . Preloader-предзагрузчик по умолчанию отвечает за визуализацию второй фазы процесса запуска JavaFX-приложения — загрузки ресурсов приложения. По умолчанию среда выполнения платформы JavaFX 2.0 использует Preloader-предзагрузчик, отображающий пользователю заставку с JavaFX-компонентом ProgressBar . При этом CSS-стиль предзагрузчика по умолчанию определяется в файле deploydialogs.css библиотеки jfxrt.jar платформы JavaFX 2.0. CSS-селекторы и CSS- свойства, определяемые в файле deploydialogs.css для предзагрузчика по умолчанию, перечислены в табл. 11.1.

Таблица 11.1. CSS-селекторы и CSS-свойства,
установленные для предзагрузчика по умолчанию
Селекторы Свойства
.default-preloader -fx-background-color ,
-fx-text-fill
.default-preloader .progress-bar -fx-skin ,
-fx-background-color
.default-preloader .progress-bar .bar -fx-background-color ,
-fx-background-insets ,
-fx-padding
.default-preloader .progress-bar .track -fx-background-color ,
-fx-border-color ,
-fx-opacity

Для изменения CSS-стиля предзагрузчика по умолчанию откроем файл jfx-impl.xml из папки nbproject каталога NetBeans-проекта JavaFX-приложения и внесем следующий код: » mainClass=»$» fallbackClass=»$»> -fx-background-color: #00bfff; -fx-preloader-text: ‘Loading Application!’; -fx-font: bold 20pt Arial; >»/> &parameters-impl;

300 Глава 11

Таким образом, изменяя параметр javafx.default.preloader.stylesheet приложения, можно изменять CSS-стиль предзагрузчика по умолчанию. Выбрав в среде NetBeans опцию проекта Очистить и построить и открыв HTML-страницу из папки dist каталога проекта, можно увидеть заставку загрузки кода JavaFX-приложения (рис. 11.2). Рис. 11.2. JavaFX-приложение с измененным CSS-стилем предзагрузчика по умолчанию П РИМЕЧАНИЕ Проект JavaFXApplicationWithCSSPreloader с примером предзагрузчика по умолчанию с измененным CSS-стилем находится в папке Примеры\Глава11 в электронном архиве (см. приложение в конце книги) . Для того чтобы создать свой Preloader-предзагрузчик, откроем среду NetBeans и в меню Файл выберем Создать проект | JavaFX | JavaFX Preloader , нажмем кнопку Далее , введем имя проекта и нажмем кнопку Готово . NetBeans-шаблон JavaFX Preloader создает класс, расширяющий абстрактный класс javafx.application.Preloader , с переопределением методов start() , handleStateChangeNotification() и handleProgressNotification() . В методе start() можно создать сцену, представляющую заставку, в методе handleStateChangeNotification() при переходе процесса запуска приложения в состояние BEFORE_INIT , BEFORE_LOAD или BEFORE_START можно закрыть окно заставки, а в методе handleProgressNotification() — передать значение прогресса запуска приложения, например, в индикатор сцены.

Заставка запуска JavaFX-приложения 301

Кроме того, в расширении класса Preloader можно переопределить методы init() , stop() , handleApplicationNotification() и handleErrorNotification() для инициализа- ции предзагрузчика, освобождения его ресурсов, обработки уведомлений от приложения и обработки ошибок запуска приложения. В окне редактора среды NetBeans изменим код сгенерированной основы класса предзагрузчика (листинг 11.1). Листинг 11.1. Код класса предзагрузчика package javafxpreloader; import javafx.application.Preloader; import javafx.application.Preloader.ProgressNotification; import javafx.application.Preloader.StateChangeNotification; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.ProgressIndicator; import javafx.scene.layout.BorderPane; import javafx.scene.paint.Color; import javafx.stage.Stage; public class JavaFXPreloader extends Preloader < private Stage stage; private ProgressIndicator progress; @Override public void start(Stage stage) throws Exception < this.stage = stage; Group root = new Group(); root.setTranslateX(200); root.setTranslateY(200); Scene scene = new Scene(root, 800, 600, Color.BEIGE); BorderPane pane = new BorderPane(); pane.setPrefSize(200,200); progress=new ProgressIndicator(); progress.setStyle("-fx-font:bold 20pt Arial;"); pane.setCenter(progress); root.getChildren().addAll(pane); stage.setScene(scene); stage.show(); >@Override public void handleStateChangeNotification(StateChangeNotification scn) < /*if (scn.getType() == StateChangeNotification.Type.BEFORE_START) < stage.hide(); >*/ > @Override public void handleProgressNotification(ProgressNotification pn) < progress.setProgress(pn.getProgress()); >

302 Глава 11

@Override public void handleApplicationNotification(Preloader.PreloaderNotification info)< Preloader.ProgressNotification ntf=(Preloader.ProgressNotification) info; if (ntf.getProgress()==1.0) stage.hide(); else progress.setProgress(-1); >> В методе start() класса предзагрузчика создается сцена с круглым индикатором ProgressIndicator , в методе handleProgressNotification() значение прогресса загруз- ки ресурсов приложения передается в индикатор сцены ProgressIndicator , в методе handleApplicationNotification() обрабатывается значение прогресса инициализации самого приложения, передаваемое из приложения, при этом по завершении инициализации приложения окно заставки закрывается. Таким образом, созданный предзагрузчик отвечает за визуализацию не только второй фазы процесса запуска JavaFX-приложения — загрузки ресурсов приложения, но и за визуализацию третьей фазы — инициализации приложения. Для передачи предзагрузчику значения прогресса инициализации приложения в главный класс JavaFX-приложения включим код: @Override public void init() throws Exception < this.notifyPreloader(new Preloader.ProgressNotification(0.0)); Thread.sleep(2000); this.notifyPreloader(new Preloader.ProgressNotification(1.0)); >Для того чтобы связать созданный предзагрузчик с JavaFX-приложением, в окне Проекты среды NetBeans щелкнем правой кнопкой мыши на узле проекта и выберем Свойства | Run . В строке Use Preloader нажмем кнопку Browse , затем кнопку Далее и выберем проект предзагрузчика, после чего нажмем кнопку Готово . Закроем окно свойств проекта кнопкой OK и выберем для проекта JavaFX- приложения опцию Очистить и построить . В результате среда NetBeans создаст в каталоге dist проекта папку lib с JAR-файлом предзагрузчика и включит в манифест JAR-файла JavaFX-приложения строки: Manifest-Version: 1.0 JavaFX-Version: 2.0 implementation-vendor: implementation-title: JavaFXApplicationWithPreloader implementation-version: 1.0 JavaFX-Preloader-Class: javafxpreloader.JavaFXPreloader JavaFX-Application-Class: javafxapplication.JavaFXApplication JavaFX-Class-Path: lib/JavaFXPreloader.jar JavaFX-Fallback-Class: com.javafx.main.NoJavaFXFallback Created-By: JavaFX Packager Main-Class: com/javafx/main/Main

Заставка запуска JavaFX-приложения 303

Кроме того, JNLP-файл каталога dist также дополнится строками: Теперь после запуска JavaFX-приложения можно будет увидеть заставку загрузки и инициализации приложения (рис. 11.3). Рис. 11.3. Заставка загрузки и инициализации JavaFX-приложения с пользовательским предзагрузчиком П РИМЕЧАНИЕ Проекты JavaFXApplicationWithPreloader и JavaFXPreloader с примером JavaFX-приложения и его предзагрузчиком находятся в папке Примеры\Глава11 в электронном архиве (см. при- ложение в конце книги) .

304 Глава 11

Г Л АВ А 12 Язык FXML Платформа JavaFX 2.0 представляет альтернативный Java-коду способ создания графа сцены JavaFX-приложения с помощью языка FXML, являющегося декларативным языком, основанным на языке XML. Применение языка FXML находится в русле традиций использования декларативных языков RIA-платформ для описания GUI-интерфейсов. Для платформы Flex — это язык MXML, для платформы Silverlight — это язык XAML. Использование языка FXML заключается в создании FXML-файла, содержащего описание дочерних узлов корневого узла графа сцены и загрузки его с помощью статического метода load() класса javafx.fxml.FXMLLoader , возвращающего корневой узел графа сцены: Parent root = FXMLLoader.load(getClass().getResource(«application.fxml»)); stage.setScene(new Scene(root)); stage.show(); Преимущество такой модели создания GUI-интерфейса состоит в том, что изменение графа сцены JavaFX-приложения не требует перекомпиляции Java-кода — FXML-описание GUI-интерфейса загружается при запуске JavaFX-приложения. Среда разработки NetBeans c поддержкой платформы JavaFX 2.0 содержит NetBeans-шаблон JavaFX FXML Application для создания JavaFX-приложений, GUI-интерфейс которых основан на FXML-описании. Для того чтобы воспользоваться данным NetBeans-шаблоном, необходимо открыть среду NetBeans c поддержкой платформы JavaFX 2.0 и в меню Файл выбрать Соз- дать проект | JavaFX | JavaFX FXML Application , нажать кнопку Далее , ввести имя проекта, отметить флажок Create Application Class и нажать кнопку Готово . В результате средой NetBeans будет сгенерирован проект, содержащий в каталоге src папку пакета главного класса приложения с тремя файлами — Java-файлом главного класса приложения, Sample.fxml и Sample.java. Главный класс приложения расширяет класс javafx.application.Application с переопределением его метода start() и имеет метод main() — точку входа в приложение. При этом в методе start() производится загрузка FXML-файла Sample.fxml,

javafx.fxml.Initializable ,

306 Глава 12

содержащего описание графа сцены приложения, с созданием корневого узла графа сцены с помощью статического метода load() класса javafx.fxml.FXMLLoader . FXML-файл Sample.fxml содержит описание корневой JavaFX-панели AnchorPane с дочерними JavaFX-компонентами Button и Label . Атрибут onAction компонента Button ссылается на метод handleButtonAction() , обрабатывающий нажатие кнопки Button , класса Sample , который определен в качестве контроллера атрибутом fx:controller компонента AnchorPane . Язык FXML поддерживает шаблон проектирования Model-View-Controller (MVC), разделяющий бизнес-логику, представление данных и взаимодействие с пользователем. В MVC-терминологии FXML-описание является представлением View, модель Model представляет компонент JavaFX Beans, обеспечивающий данные JavaFXприложения, а контроллером Controller является Java-класс, реализующий код взаимодействия с пользователем и связывания компонента JavaFX Beans с FXMLописанием GUI-интерфейса JavaFX-приложения. Для включения такого контроллера в JavaFX-приложение его необходимо указать в качестве значения атрибута fx:controller FXML-описания. FXML-контроллер может реализовывать интерфейс имеющий единственный метод void initialize(java.net.URL location, java.util. ResourceBundle resources) , вызываемый средой выполнения для инициализации класса FXML-контроллера и используемый, например, для связывания компонента данных JavaFX Beans с FXML-описанием: private Person person = new Person(); @FXML private TextField firstName; @Override public void initialize(URL url, ResourceBundle rb) < firstName.textProperty().bindBidirectional(person.firstNameProperty());>Методу initialize() , в качестве аргументов, среда выполнения передает URL-адрес FXML-описания GUI-интерфейса JavaFX-приложения и ResourceBundle-ресурсы JavaFX-приложения. URL-адрес FXML-описания и ResourceBundle-ресурсы определяются при вызове в главном классе JavaFX-приложения статического метода load(java.net.URL location, java.util.ResourceBundle resources) класса javafx.fxml.FXMLLoader . Файл ResourceBundle-ресурсов решает задачу локализации строк GUI-интерфейса и передается как ResourceBundle -объект методу load() с помощью статического метода getBundle() класса java.util.ResourceBundle : Parent root = FXMLLoader.load(getClass().getResource(«[имя файла FXMLописания].fxml»), ResourceBundle.getBundle(«[имя пакета].[имя properties-файла]»));

Для создания файла ResourceBundle-ресурсов в среде NetBeans щелкнем правой кнопкой мыши на узле пакета главного класса JavaFX FXML-приложения и выбе-

Язык FXML 307

рем Создать | Другое | Другое | Файл свойств , нажмем кнопку Далее , введем имя файла и нажмем кнопку Готово . В результате средой NetBeans будет сгенерирован properties-файл, имя которого без расширения, но предваряя именем пакета, нужно указать в аргументе метода ResourceBundle.getBundle() . Заполнив файл ResourceBundle-ресурсов парами «ключ — значение», получить доступ к любому значению можно с помощью метода getString(String key) класса java.util.ResourceBundle в Java-коде или указанием префикса % и имени ключа в качестве значения атрибута компонента FXML-описания:  properties-файл: labelText=Hello  файл FXML-описания: Помимо интерфейса javafx.fxml.Initializable FXML-контроллер может использовать аннотацию @FXML пакета javafx.fxml, которая маркирует protected — или private — поля и методы FXML-контроллера, доступные из FXML-описания:  файл FXML-описания: #handleButtonAction » fx:id=»button» />  класс FXML-контроллера: @FXML private void handleButtonAction (ActionEvent event) < System.out.println("You clicked me!");>Чтобы не применять аннотацию @FXML , поля и методы FXML-контроллера, доступные из FXML-описания, делаются публичными:  файл FXML-описания: #handleButtonAction » fx:id=»button» />  класс FXML-контроллера: public void handleButtonAction (ActionEvent event) < System.out.println("You clicked me!");>FXML-описание GUI-интерфейса JavaFX-приложения имеет структуру XMLдокумента, однако не имеет XML-схемы. Для использования специфических XMLэлементов пространства имен языка FXML в FXML-описание должно быть вклю- чено объявление пространства имен xmlns:fx=»http://javafx.com/fxml». Экземпляр JavaFX-компонента создается в FXML-описании с помощью тега импорта и тега, начинающегося с имени класса JavaFX-компонента:

308 Глава 12

FXML-описание позволяет создавать не только экземпляры JavaFX-компонентов, но и другие Java-объекты. Экземпляр хэш-таблицы java.util.HashMap создается с помощью тега: Экземпляр Java-класса, имеющего статический метод valueOf() , создается с помощью указания атрибута fx:value : Java-объект, возвращаемый методом класса-фабрики, создается указанием атрибута fx:factory : . . . Кроме того, для создания экземпляров классов можно переопределить фабрику javafx.fxml.JavaFXBuilderFactory , используемую по умолчанию для создания JavaFX-объектов. Для этого при вызове в главном классе JavaFX-приложения ста- тического метода load(java.net.URL location, java.util.ResourceBundle resources, BuilderFactory builderFactory) класса javafx.fxml.FXMLLoader необходимо в качестве аргумента указать экземпляр пользовательского класса, реализующего интерфейс javafx.util.BuilderFactory . Интерфейс BuilderFactory имеет единственный метод getBuilder() , возвращающий экземпляр класса, который реализует интерфейс javafx.util.Builder . Поэтому необходимо также создать пользовательский класс, реализующий интерфейс Builder . Интерфейс Builder имеет единственный метод build() , возвращающий Java-объект. Тег языка FXML обеспечивает модульность FXML-описания и указывает своим атрибутом source имя включаемого FXML-файла, содержащего описание создаваемых JavaFX-компонентов. Тег языка FXML своим атрибутом source позволяет сослаться на другой именованный FXML-элемент. Атрибуты FXML-элементов могут быть нескольких типов. Атрибут FXML-элемента может описывать свойство JavaFX-компонента. Альтернативно FXML-атрибуту свойство JavaFX-компонента может описывать вложенный тег FXML-элемента:

Язык FXML 309

 префикс @ представляет URL-адрес:  префикс % указывает ссылку на ключ ResourceBundle-ресурса:  префикс $ указывает ссылку на именованную переменную: . Тег языка FXML позволяет создавать объекты, на которые можно ссылаться из любого уровня графа сцены;  префикс $ вместе с фигурными скобками определяет связывание данных: «/> Атрибут FXML-элемента может описывать статические свойства класса, при этом атрибут имеет вид: [имя класса].[имя свойства]=»[значение]» Атрибут FXML-элемента также может описывать обработчик событий JavaFXкомпонента:  FXML-контроллер: public void handleButtonAction(ActionEvent event) < > FXML-описание: или function handleButtonAction(event) Тег языка FXML обеспечивает включение в FXML-описание кода JVM- языков, таких как JavaScript, Groovy и др. Скриптовый код может быть включен непосредственно в тег или содержаться в отдельном файле, который включается в FXML-описание атрибутом source тега .

310 Глава 12

П Р И Л О Ж Е Н И Е Описание электронного архива Электронный архив к книге выложен на FTP-сервере издательства по адресу: ftp://85.249.45.166/9785977508209.zip . Ссылка доступна и со страницы книги на сайте www.bhv.ru . В архиве находятся следующие папки:  папка Приложения содержит файл с описанием пакетов программного интер- фейса JavaFX 2.0 API;

 папка Примеры содержит рассмотренные в книге проекты, перечисленные
в табл. П1.
Таблица П1. Содержимое папки Примеры
Папка Описание
\Глава2
JavaFXApplicationAccordion Пример создания панели Accordion
JavaFXApplicationAnchorPane Пример создания панели AnchorPane
JavaFXApplicationAreaChart Пример создания диаграммы AreaChart
JavaFXApplicationAudioClip Пример звукового сопровождения
JavaFXApplicationBarChart Пример создания диаграммы BarChart
JavaFXApplicationBorderPane Пример создания панели BorderPane
JavaFXApplicationBoxPane Пример создания панелей VBox и HBox
JavaFXApplicationBubbleChart Пример создания диаграммы BubbleChart
JavaFXApplicationButton Пример создания кнопки Button
JavaFXApplicationCheckBox Пример создания флажка CheckBox
JavaFXApplicationChoiceBox Пример создания списка ChoiceBox
JavaFXApplicationContextMenu Пример создания контекстного меню ContextMenu
JavaFXApplicationFileChooser Пример создания окна FileChooser
JavaFXApplicationFlowPane Пример создания панели FlowPane
312 Приложение
Таблица П1 (продолжение)
Папка Описание
JavaFXApplicationGridPane Пример создания панели GridPane
JavaFXApplicationGroup Пример создания узла Group
JavaFXApplicationHTMLEditor Пример создания редактора HTML-контента
HTMLEditor
JavaFXApplicationHyperlink Пример создания гиперссылки Hyperlink
JavaFXApplicationImageView Пример создания узла изображения ImageView
JavaFXApplicationLabel Пример создания метки Label
JavaFXApplicationLineChart Пример создания диаграммы LineChart
JavaFXApplicationListView Пример создания списка ListView
JavaFXApplicationMediaView Пример воспроизведения медиаконтента
JavaFXApplicationMenuBar Пример создания панели MenuBar
JavaFXApplicationMenuButton Пример создания кнопки MenuButton
JavaFXApplicationPasswordField Пример создания поля PasswordField
JavaFXApplicationPieChart Пример создания диаграммы PieChart
JavaFXApplicationPopup Пример создания окна Popup
JavaFXApplicationProgress Пример создания индикаторов ProgressBar
и ProgressIndicator
JavaFXApplicationRadioButton Пример создания переключателя RadioButton
JavaFXApplicationScatterChart Пример создания диаграммы ScatterChart
JavaFXApplicationScene Пример создания сцены Scene
JavaFXApplicationScrollPane Пример создания панели ScrollPane
JavaFXApplicationSeparator Пример создания разделителя Separator
JavaFXApplicationShape Пример создания геометрических форм Arc , Circle ,
CubicCurve , Ellipse , Line , Path , Polygon , Polyline ,
QuadCurve , Rectangle , SVGPath
JavaFXApplicationSlider Пример создания ползунка Slider
JavaFXApplicationSplitMenuButton Пример создания кнопки SplitMenuButton
JavaFXApplicationSplitPane Пример создания панели SplitPane
JavaFXApplicationStackPane Пример создания панели StackPane
JavaFXApplicationStage Пример создания окна Stage
JavaFXApplicationTableView Пример создания таблицы TableView
JavaFXApplicationTabPane Пример создания панели TabPane
JavaFXApplicationText Пример создания узла Text
JavaFXApplicationTextArea Пример создания поля TextArea
JavaFXApplicationTextField Пример создания поля TextField
Описание электронного архива 313
Таблица П1 (продолжение)
Папка Описание
JavaFXApplicationTilePane Пример создания панели TilePane
JavaFXApplicationTitledPane Пример создания панели TitledPane
JavaFXApplicationToggleButton Пример создания кнопки ToggleButton
JavaFXApplicationToolBar Пример создания панели ToolBar
JavaFXApplicationTooltip Пример создания подсказки Tooltip
JavaFXApplicationTreeView Пример создания дерева TreeView
JavaFXApplicationWebView Пример создания встроенного Web-браузера
WebView
\Глава4
JavaFXApplicationBlend Пример эффекта Blend
JavaFXApplicationBloom Пример эффекта Bloom
JavaFXApplicationBoxBlur Пример эффекта BoxBlur
JavaFXApplicationColorAdjust Пример эффекта ColorAdjust
JavaFXApplicationDisplacementMap Пример эффекта DisplacementMap
JavaFXApplicationDropShadow Пример эффекта DropShadow
JavaFXApplicationGaussianBlur Пример эффекта GaussianBlur
JavaFXApplicationGlow Пример эффекта Glow
JavaFXApplicationInnerShadow Пример эффекта InnerShadow
JavaFXApplicationLighting Пример эффекта Lighting
JavaFXApplicationMotionBlur Пример эффекта MotionBlur
JavaFXApplicationPerspective Пример эффекта Perspective
JavaFXApplicationReflection Пример эффекта Reflection
JavaFXApplicationSepiaTone Пример эффекта SepiaTone
JavaFXApplicationShadow Пример эффекта Shadow
\Глава5
JavaFXApplicationAnimation Пример создания Transition — и Timeline -анимации
\Глава7
JavaFXApplicationJSObject, Примеры совместного использования JavaScript
JavaFXApplicationLiveConnect и JavaFX
JavaFXApplicationWebViewWithJavaScript Пример совместного использования JavaScript
и WebView
\Глава8
JavaFXApplicationTask Пример выполнения фоновой задачи
в JavaFX-приложении
314 Приложение
Таблица П1 (окончание)
Папка Описание
\Глава9
JavaApplicationSwing Пример Swing-приложения, содержащего
JavaFX-компоненты
JavaApplicationSwingApplet Пример JApplet-апплета, содержащего
JavaFX-компоненты
\Глава11
JavaFXApplicationWithCSSPreloader Пример предзагрузчика по умолчанию с изменен-
ным CSS-стилем
JavaFXApplicationWithSplash Пример создания заставки инициализации среды
выполнения JavaFX-апплета
JavaFXApplicationWithPreloader, Пример JavaFX-приложения и его предзагрузчика
JavaFXPreloader

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *