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