Статьи

Сосредоточьтесь на JavaFX 2 FXML с IDE NetBeans 7.1

В октябре 2011 года я использовал статью Hello JavaFX 2.0: Введение в бета-версию среды IDE NetBeans 7.1, чтобы рассмотреть использование бета- версии NetBeans IDE 7.1 для создания простого приложения JavaFX 2.0 в стиле Hello, World . В этой статье я расскажу об использовании среды IDE NetBeans 7.1 ( больше не в бета-версии ) для создания немного более сложного приложения JavaFX 2, которое интенсивно использует FXML .

В большинстве моих постов о JavaFX 2 до этого момента подчеркивалась «чистая Java» природа JavaFX 2. Примеры в этих постах были написаны на стандартной Java с использованием API JavaFX 2 непосредственно из исходного кода Java. Эти примеры также были собраны с помощью компилятора javac и выполнены с помощью средства запуска приложений java . В этой статье я возьму один из этих примеров [ (Pure Java) JavaFX 2.0 Menus ] и » перенес » его для использования FXML для макета презентации.

Для удобства я включил полный список Java для реализации «чистого Java» меню JavaFX 2.

Демонстрация меню JavaFX 2.0 (Pure Java)

package dustin.examples;

import static java.lang.System.out;

import javafx.application.Application;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

/**
 * Example of creating menus in JavaFX.
 * 
 * @author Dustin
 */
public class JavaFxMenus extends Application
{
   /**
    * Build menu bar with included menus for this demonstration.
    * 
    * @param menuWidthProperty Width to be bound to menu bar width.
    * @return Menu Bar with menus included.
    */
   private MenuBar buildMenuBarWithMenus(final ReadOnlyDoubleProperty menuWidthProperty)
   {
      final MenuBar menuBar = new MenuBar();

      // Prepare left-most 'File' drop-down menu
      final Menu fileMenu = new Menu("File");
      fileMenu.getItems().add(new MenuItem("New"));
      fileMenu.getItems().add(new MenuItem("Open"));
      fileMenu.getItems().add(new MenuItem("Save"));
      fileMenu.getItems().add(new MenuItem("Save As"));
      fileMenu.getItems().add(new SeparatorMenuItem());
      fileMenu.getItems().add(new MenuItem("Exit"));
      menuBar.getMenus().add(fileMenu);

      // Prepare 'Examples' drop-down menu
      final Menu examplesMenu = new Menu("JavaFX 2.0 Examples");
      examplesMenu.getItems().add(new MenuItem("Text Example"));
      examplesMenu.getItems().add(new MenuItem("Objects Example"));
      examplesMenu.getItems().add(new MenuItem("Animation Example"));
      menuBar.getMenus().add(examplesMenu);

      // Prepare 'Help' drop-down menu
      final Menu helpMenu = new Menu("Help");
      final MenuItem searchMenuItem = new MenuItem("Search");
      searchMenuItem.setDisable(true);
      helpMenu.getItems().add(searchMenuItem);
      final MenuItem onlineManualMenuItem = new MenuItem("Online Manual");
      onlineManualMenuItem.setVisible(false);
      helpMenu.getItems().add(onlineManualMenuItem);
      helpMenu.getItems().add(new SeparatorMenuItem());
      final MenuItem aboutMenuItem =
         MenuItemBuilder.create()
                        .text("About")
                        .onAction(
                            new EventHandler<ActionEvent>()
                            {
                               @Override public void handle(ActionEvent e)
                               {
                                  out.println("You clicked on About!");
                               }
                            })
                        .accelerator(
                            new KeyCodeCombination(
                               KeyCode.A, KeyCombination.CONTROL_DOWN))
                        .build();             
      helpMenu.getItems().add(aboutMenuItem);
      menuBar.getMenus().add(helpMenu);

      // bind width of menu bar to width of associated stage
      menuBar.prefWidthProperty().bind(menuWidthProperty);

      return menuBar;
   }

   /**
    * Start of JavaFX application demonstrating menu support.
    * 
    * @param stage Primary stage.
    */
   @Override
   public void start(final Stage stage)
   {
      stage.setTitle("Creating Menus with JavaFX 2.0");
      final Group rootGroup = new Group();
      final Scene scene = new Scene(rootGroup, 800, 400, Color.WHEAT);
      final MenuBar menuBar = buildMenuBarWithMenus(stage.widthProperty());
      rootGroup.getChildren().add(menuBar);
      stage.setScene(scene);
      stage.show();
   }

   /**
    * Main executable function for running examples.
    * 
    * @param arguments Command-line arguments: none expected.
    */
   public static void main(final String[] arguments)
   {
      Application.launch(arguments);
   }
}

Помимо добавления возможности писать приложения JavaFX на «чистой Java», как это реализовано в последнем листинге кода, JavaFX 2 также предоставляет FXML , грамматику XML для указания макета приложения JavaFX. Такой подход очень похож, обеспечиваемой Flex «s MXML , по OpenLaszlo » s LZX , и Mozilla «s XUL . Этот подход хорошо работает с иерархическим графом сцен JavaFX.

Портированный код FXML показан ниже. Легко увидеть структуру представления в этом XML.

JavaFx2Menus.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<VBox id="vbox" prefHeight="400" prefWidth="800"
      xmlns:fx="http://javafx.com/fxml"
      fx:controller="dustin.examples.MenuController">
   <MenuBar fx:id="menuBar" onKeyPressed="#handleKeyInput">
      <menus>
         <Menu text="File">
            <items>
               <MenuItem text="New"/>
               <MenuItem text="Open"/>
               <MenuItem text="Save"/>
               <MenuItem text="Save As"/>
               <SeparatorMenuItem  />
               <MenuItem text="Exit"/>
            </items>
         </Menu>
         <Menu text="JavaFX 2.0 Examples">
            <items>
               <MenuItem text="Text Example"/>
               <MenuItem text="Objects Example"/>
               <MenuItem text="Animation Example"/>
            </items>
         </Menu>
         <Menu text="Help">
            <items>
               <MenuItem text="Search" disable="true"/>
               <MenuItem text="Online Manual" visible="false"/>
               <SeparatorMenuItem />
               <MenuItem text="About" onAction="#handleAboutAction"/>
            </items>
         </Menu>
      </menus>
   </MenuBar>
</VBox>

Важную часть этого простого FXML можно просмотреть на одной странице в среде IDE NetBeans 7.1, как показано на следующем снимке экрана.

Помимо краткости FXML и эстетически привлекательного иерархического представления, FXML также должен выглядеть знакомым для всех, кто работал с Flex, OpenLaszlo или XUL. Большая часть структуры презентации легко идентифицируется в документе FXML. Этот пример включает в себя обработчик onAction, который отвечает на пункт меню «О программе», и с панелью меню также связано действие onKeyPressed (я не смог найти способ легко связать этот обработчик с самим элементом меню). Оба эти действия ссылаются на имена, которые начинаются с символа #. Имена, следующие за #, являются именами методов в классе контроллера (определяется атрибутом fx: controller в VBox как класс с именем dustin.examples.MenuController). Источник для этого класса контроллера показан ниже.

MenuController.java

package dustin.examples;

import static java.lang.System.out;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.MenuBar;
import javafx.scene.input.InputEvent;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

/**
 * Controller class for JavaFX 2 Menus with FXML post and demonstration.
 * 
 * @author Dustin
 */
public class MenuController implements Initializable
{
   @FXML
   private MenuBar menuBar;

   /**
    * Handle action related to "About" menu item.
    * 
    * @param event Event on "About" menu item.
    */
   @FXML
   private void handleAboutAction(final ActionEvent event)
   {
      provideAboutFunctionality();
   }

   /**
    * Handle action related to input (in this case specifically only responds to
    * keyboard event CTRL-A).
    * 
    * @param event Input event.
    */
   @FXML
   private void handleKeyInput(final InputEvent event)
   {
      if (event instanceof KeyEvent)
      {
         final KeyEvent keyEvent = (KeyEvent) event;
         if (keyEvent.isControlDown() && keyEvent.getCode() == KeyCode.A)
         {
            provideAboutFunctionality();
         }
      }
   }

   /**
    * Perform functionality associated with "About" menu selection or CTRL-A.
    */
   private void provideAboutFunctionality()
   {
      out.println("You clicked on About!");      
   }

   @Override
   public void initialize(final URL url, final ResourceBundle rb)
   {
      menuBar.setFocusTraversable(true);
   }   
}

Приведенный выше код из класса контроллера включает в себя два метода, на которые ссылаются как на обработчики действий в FXML с префиксами #. Эти два метода являются закрытыми и обычно не будут видны загрузчику FXML, но использование аннотации @FXML решает эту проблему. Атрибут menuBar также является закрытым, но видимым для загрузчика FXML благодаря собственной аннотации @FXML.

Чтобы приложение отвечало на CTRL-A перед любым начальным доступом к строке меню, я обнаружил, что мне нужно иметь ссылку на строку меню в контроллере и вызвать setFocusTraversable (true) для этого экземпляра. Без этого CTRL-A будет отвечать только в том случае, если я впервые нажму на строку меню один раз. Крюком от FXML к этому атрибуту в контроллере является атрибут fx: id, используемый в FXML. Этот атрибут ссылается на имя «menuBar», которое (не случайно) также является именем атрибута в классе контроллера. Другими словами, атрибут fx: id = «menuBar» связывает элемент XML, который включен в FXML, с атрибутом с тем же именем в контроллере.

В этом посте я не показывал многое, касающееся среды IDE NetBeans 7.1, но стоит отметить, что у меня были все соответствующие файлы, созданные в новом проекте с использованием мастера создания проектов NetBeans IDE 7.1 и выбора приложения JavaFX FXML в качестве типа проекта. , Это создало основной файл FXML, основной файл контроллера и основной класс для загрузки FXML и запуска приложения. Я изменил их в приведенных выше примерах для FXML и для класса контроллера. Модифицированный класс, который загружает FXML и запускает приложение JavaFX, показан ниже. (Там не так много!)

JavaFxMenusWithFxmlDemo.java

package dustin.examples;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

/**
 * Simple example of JavaFX 2 FXML used to demonstrate JavaFX 2 menus. This is a
 * "port" of the example featured using "pure Java" (no FXML) in the post
 * "(Pure Java) JavaFX 2.0 Menus"
 * (http://marxsoftware.blogspot.com/2011/12/pure-java-javafx-20-menus.html).
 * 
 * @author Dustin
 */
public class JavaFxMenusWithFxmlDemo extends Application
{
   /**
    * Main function for running this application.
    * 
    * @param arguments Command-line arguments: none expected.
    */
   public static void main(String[] arguments)
   {
      Application.launch(JavaFxMenusWithFxmlDemo.class, arguments);
   }

   /**
    * Overridden Application.start(Stage) method.
    * 
    * @param stage Primary stage.
    * @throws Exception JavaFX application exception.
    */
   @Override
   public void start(final Stage stage) throws Exception
   {
      final Parent fxmlRoot = FXMLLoader.load(getClass().getResource("JavaFx2Menus.fxml"));
      stage.setScene(new Scene(fxmlRoot));
      stage.show();
   }
}

Я не буду показывать здесь вывод, потому что это по сути то, что произвела версия «чистой Java», которая показана в моем посте об этом подходе. Помимо создания файлов, которые я адаптировал для своего приложения, мастер новых проектов NetBeans IDE 7.1 для приложения JavaFX FXML также создал проект, который автоматически упаковывает то, что мне нужно для приложения JavaFX, в исполняемый файл JAR. Содержимое JAR в этом случае показано на следующем снимке экрана.

Самая хитрая часть об использовании FXML — это относительно менее доступная документация. Чистые API Java имеют сильную документацию Javadoc API , но я не знаю ничего подобного для FXML (включая документацию или схему для пространства имен http://javafx.com/fxml ). Есть несколько замечательных ресурсов FXML, таких как Начало работы с FXML и Введение в FXML . Возможно, лучшим документом для выяснения того, какие элементы и атрибуты предоставляет FXML, является Представление FXML: Язык разметки для JavaFX , который объясняет «отображение» стандартных классов Java API JavaFX на элементы и атрибуты JavaFX FXML.

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

От http://marxsoftware.blogspot.com/2012/01/focus-on-javafx-2-fxml-with-netbeans-71.html