Статьи

Создание пользовательских компонентов JavaFX с помощью Scene Builder и FXML.

Одна из целей нашей разработки с нашим приложением JavaFX — попытаться сохранить как можно больше дизайна  пользовательского интерфейса в инструменте дизайна пользовательского интерфейса Scene Builder и бизнес-логику для приложения в Java. Одна из вещей, которая не совсем понятна, — это как создать новый компонент в Scene Builder, а затем повторно использовать этот компонент в других компонентах, созданных в Scene Builder. 

В приведенном ниже примере я иллюстрирую, как создать пользовательскую таблицу и компоненты «Добавить план», а затем добавить их в новую сцену с помощью FXML. 

Первым шагом является создание простого компонента, который действует как виджет «Добавить план» в пользовательском интерфейсе. Виджет содержит AnchorPane, ImageView и Label.

образ

Для следующего шага откройте файл FXML и измените первое объявление «AnchorPane» на «<fx: root…», как в примере кода ниже. Тип должен совпадать с классом controller / root, который я проиллюстрирую ниже. В этом случае класс root / controller будет расширять javafx.scene.layout.AnchorPane

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.text.*?>
<fx:root type="javafx.scene.layout.AnchorPane" xmlns:fx="http://javafx.com/fxml">
  <children>
    <AnchorPane fx:id="myTestButton" layoutX="0.0" layoutY="5.0" minHeight="58.0" prefHeight="74.0" prefWidth="129.0244140625" styleClass="main-back">
      <children>
        <ImageView fitHeight="44.0" fitWidth="44.0" layoutX="43.0" layoutY="8.0" pickOnBounds="true" preserveRatio="true">
          <image>
            <Image url="@new-plan.png" preserveRatio="false" smooth="false" />
          </image>
        </ImageView>
        <Label layoutX="43.0" layoutY="52.0" text="Add Plan" textFill="WHITE">
          <font>
            <Font name="System Bold" size="12.0" />
          </font>
        </Label>
      </children>
      <effect>
        <DropShadow />
      </effect>
    </AnchorPane>
  </children>
  </fx:root>

Создайте класс, который будет выступать как корнем компонента, так и его контроллером. Класс должен расширять тип, который был ранее определен в файле FXML. Используйте загрузчик FXML для установки корня и контроллера компонента в класс «this», а затем загрузите файл FXML компонента.

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.lynden.fx.test;

import com.lynden.ui.util.UIUtilities;
import java.io.IOException;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.AnchorPane;/**
*
* @author ROBT
*/
public class TestButton extends AnchorPane {

  @FXML
  private AnchorPane myTestButton;

 

public TestButton() {
  FXMLLoader fxmlLoader = new FXMLLoader(

getClass().getResource("/com/lynden/planning/ui/TestButton.fxml"));
 

fxmlLoader.setRoot(this);
  fxmlLoader.setController(this);

  try {
  fxmlLoader.load();
  } catch (IOException exception) {
  throw new RuntimeException(exception);
  }
  }
}

Далее, 2-й компонент представляет собой TableView, который содержит коллекцию bean-компонентов и отображает их соответствующие данные. 

образ

Как и в случае с последним компонентом, первое объявление <AnchorPane> изменяется на <fx: root>

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

<?import com.lynden.fx.*?>
<?import com.lynden.fx.table.*?>
<?import com.lynden.fx.test.*?>
<?import com.lynden.planning.ui.*?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.collections.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.cell.*?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.shape.*?>
<?import javafx.scene.text.*?>
<?import javafx.util.*?>
<?scenebuilder-classpath-element split-flip-1.0.0.jar?>

<fx:root type="javafx.scene.layout.AnchorPane" xmlns:fx="http://javafx.com/fxml">
  <children>
    <BorderPane layoutX="0.0" layoutY="0.0" prefHeight="400.0" prefWidth="600.0">
      <center>
        <TableView fx:id="myTableView" prefHeight="200.0" prefWidth="200.0">
          <columns>
            <TableColumn prefWidth="42.0" text="">
            </TableColumn>
            <TableColumn prefWidth="110.0" text="Plan">
              <cellValueFactory>
                <PropertyValueFactory property="vfcPlan" />
              </cellValueFactory>
            </TableColumn>
            <TableColumn prefWidth="55.0" text="VFC #">
              <cellValueFactory>
                <PropertyValueFactory property="vfcNumber" />
              </cellValueFactory>
            </TableColumn>
            <TableColumn prefWidth="100.0" text="Location">
              <cellValueFactory>
                <PropertyValueFactory property="location" />
              </cellValueFactory>
            </TableColumn>
            <TableColumn prefWidth="100.0" text="Arrival">
              <cellValueFactory>
                <PropertyValueFactory property="arrivalTime" />
              </cellValueFactory>
            </TableColumn>
            <TableColumn prefWidth="35.0" text="Org">
              <cellValueFactory>
                <PropertyValueFactory property="origin" />
              </cellValueFactory>
            </TableColumn>
            <TableColumn prefWidth="35.0" text="Dst">
              <cellValueFactory>
                <PropertyValueFactory property="destination" />
              </cellValueFactory>
            </TableColumn>
            <TableColumn prefWidth="65.0" text="Shipments">
              <cellValueFactory>
                <PropertyValueFactory property="shipments" />
              </cellValueFactory>
            </TableColumn>
            <TableColumn prefWidth="125.0" text="Consignee">
              <cellValueFactory>
                <PropertyValueFactory property="consignee" />
              </cellValueFactory>
            </TableColumn>
            <TableColumn prefWidth="35.0" text="Haz">
              <cellValueFactory>
                <PropertyValueFactory property="hazMat" />
              </cellValueFactory>
            </TableColumn>
            <TableColumn prefWidth="45.0" text="Temp">
              <cellValueFactory>
                <PropertyValueFactory property="temperature" />
              </cellValueFactory>
            </TableColumn>
          </columns>
          <items>
            <FXCollections fx:factory="observableArrayList">
              <InboundBean arrivalTime="03-11-13 15:00" consignee="FMeyer" destination="ANC" hazMat="N" location="Consignee" origin="TAC" shipments="1" temperature="KFF" vfcNumber="345440" vfcPlan="Fred Meyer" />
              <InboundBean arrivalTime="03-11-13 15:00" consignee="FMeyer" destination="ANC" hazMat="N" location="Yard" origin="TAC" shipments="1" temperature="KFF" vfcNumber="123456" vfcPlan="Fred Meyer" />
              <InboundBean arrivalTime="03-11-13 15:00" consignee="FMeyer" destination="ANC" hazMat="N" location="Trip 12543" origin="TAC" shipments="1" temperature="KFF" vfcNumber="235555" vfcPlan="Fred Meyer" />
              <InboundBean arrivalTime="03-11-13 15:00" consignee="Costco" destination="KNA" hazMat="N" location="Trip 551332" origin="TAC" recovered="false" shipments="1" temperature="KFF" vfcNumber="244000" vfcPlan="KNA" />
              <InboundBean arrivalTime="03-11-13 15:00" consignee="SBS" destination="ANC" hazMat="N" location="Trip 12543" origin="TAC" recovered="false" shipments="1" temperature="KFF" vfcNumber="291007" vfcPlan="Sealand" />
              <InboundBean arrivalTime="03-11-13 15:00" consignee="Costco" destination="FBK" hazMat="N" location="Yard" origin="TAC" recovered="true" shipments="2" temperature="KFF" vfcNumber="291008" vfcPlan="Fairbanks" />
              <InboundBean arrivalTime="03-11-13 15:00" consignee="" destination="KNA" hazMat="N" location="Trip 12543" origin="TAC" recovered="false" shipments="1" temperature="KFF" vfcNumber="234554" vfcPlan="" />
              <InboundBean arrivalTime="03-11-13 15:00" consignee="" destination="ANC" hazMat="N" location="2" origin="TAC" recovered="false" shipments="2" temperature="KFF" vfcNumber="123407" vfcPlan="Darigold" />
              <InboundBean arrivalTime="03-11-13 15:00" consignee="" destination="FBK" hazMat="N" location="Yard" origin="TAC" recovered="true" shipments="1" temperature="KFF" vfcNumber="233458" vfcPlan="Yard" />
              <InboundBean arrivalTime="03-11-13 15:00" consignee="" destination="KNA" hazMat="N" location="" origin="TAC" recovered="false" shipments="1" temperature="KFF" vfcNumber="244340" vfcPlan="Flat" />
              <InboundBean arrivalTime="03-11-13 15:00" consignee="" destination="ANC" hazMat="Y" location="Trip 12543" origin="TAC" recovered="false" shipments="2" temperature="KFF" vfcNumber="222347" vfcPlan="" />
              <InboundBean arrivalTime="03-11-13 15:00" consignee="" destination="FBK" hazMat="N" location="" origin="TAC" recovered="false" shipments="1" temperature="KFF" vfcNumber="312008" vfcPlan="" />
              <InboundBean arrivalTime="03-11-13 15:00" consignee="" destination="KNA" hazMat="N" location="Trip 551332" origin="TAC" recovered="false" shipments="1" temperature="KFF" vfcNumber="313000" vfcPlan="" />
              <InboundBean arrivalTime="03-11-13 15:00" consignee="SBS" destination="ANC" hazMat="N" location="" origin="TAC" recovered="false" shipments="2" temperature="KFF" vfcNumber="334507" vfcPlan="" />
              <InboundBean arrivalTime="03-11-13 15:00" consignee="" destination="FBK" hazMat="N" location="" origin="TAC" recovered="false" shipments="1" temperature="KFF" vfcNumber="244408" vfcPlan="" />
            </FXCollections>
          </items>
        </TableView>
      </center>
    </BorderPane>
  </children>
</fx:root>

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

/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.lynden.fx.test; import com.lynden.fx.InboundBean; import java.io.IOException; import javafx.event.EventHandler; import javafx.event.EventType; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.control.TableView; import javafx.scene.input.ClipboardContent; import javafx.scene.input.Dragboard; import javafx.scene.input.MouseEvent; import javafx.scene.input.TransferMode; import javafx.scene.layout.AnchorPane; /** * * @author ROBT */ public class TestTable extends AnchorPane { @FXML private TableView myTableView; public TestTable() { FXMLLoader fxmlLoader = new FXMLLoader(

getClass().getResource("/com/lynden/planning/ui/TestTable.fxml"));

fxmlLoader.setRoot(this); fxmlLoader.setController(this); try { fxmlLoader.load(); } catch (IOException exception) { throw new RuntimeException(exception); } } }

Наконец, можно создать новую сцену, которая включает компоненты TestTable и TestButton, которые были созданы выше. К сожалению, пользовательские компоненты не могут быть добавлены в палитру Scene Builder, поэтому их необходимо вставить вручную в файл FXML. Вам просто нужно убедиться, что у вас определены правильные операторы импорта, а компоненты TestTable и TestButton могут быть вставлены в код FXML, как и любой собственный компонент JavaFX.

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

<?import com.lynden.fx.*?>
<?import com.lynden.fx.table.*?>
<?import com.lynden.fx.test.*?>
<?import com.lynden.planning.ui.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<?scenebuilder-classpath-element ../../../../../../../target/classes?>

<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="766.0" xmlns:fx="http://javafx.com/fxml">
    <children>
        <TestTable layoutX="5.0" layoutY="4.0" />
        <TestButton layoutX="622.0" layoutY="157.0" />
    </children>
</AnchorPane>

Когда Scene Builder откроет файл FXML, вы увидите, что оба компонента отображаются в новой сцене

образ

Вот и все, надеюсь, это поможет другим, кто рассматривает возможность добавления пользовательских компонентов в свои пользовательские интерфейсы, разработанные с помощью Scene Builder.

twitter:  @RobTerp