Одна из целей нашей разработки с нашим приложением 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


