여기에는 몇 가지 방법이 있습니다.
하나는 XML 파일을 간단히 구문 분석하고 Java 코드로 FX 컨트롤을 만드는 것입니다. 이것은 너무 나쁘지는 않지만 FXML이 전혀 없을 것입니다. 기본 아이디어는 DocumentBuilder
을 만들고 XML 파일의 메모리 모델 인 Document
으로 XML 파일을 구문 분석하는 데 사용하는 것입니다. 이를 사용하여 xml 요소를 반복하고 각 xml 요소에 적절한 JavaFX UI 요소를 만들고 Pane
에 추가 할 수 있습니다.
다른 방법은 Extensible Stylesheet Language Transformation을 사용하여 XML
파일을 FXML
으로 변환하는 것입니다. 난 분명이 기술 전문가는 생각하지 않지만, 아이디어는 매우 간단합니다 :
는 기본적 FXML
파일이 xml
파일의 내용을 기반으로 어떻게 보일지를 정의하는 xsl
파일을 정의합니다. 다시 말하지만, 나는 xsl
의 세부 정말 익숙하지 않은,하지만 이런 일이 예를 들어 작업이 나타납니다 :
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fx="http://javafx.com/fxml">
<xsl:template match="/">
<xsl:processing-instruction name="import">
java.lang.*
</xsl:processing-instruction>
<xsl:processing-instruction name="import">
javafx.scene.layout.*
</xsl:processing-instruction>
<xsl:processing-instruction name="import">
javafx.scene.control.*
</xsl:processing-instruction>
<xsl:processing-instruction name="import">
javafx.geometry.Insets
</xsl:processing-instruction>
<xsl:processing-instruction name="import">
javafx.collections.FXCollections
</xsl:processing-instruction>
<GridPane hgap="5" vgap="5" fx:id="form" fx:controller="xml2fx.FormController">
<columnConstraints>
<ColumnConstraints halignment="RIGHT" hgrow="NEVER" />
<ColumnConstraints halignment="LEFT" hgrow="ALWAYS" />
</columnConstraints>
<padding>
<Insets top="10" bottom="10" left="10" right="10"/>
</padding>
<xsl:apply-templates select="//text"/>
<xsl:apply-templates select="//question"/>
</GridPane>
</xsl:template>
<xsl:template match="text">
<Label text="{.}" wrapText="true" textAlignment="RIGHT"
GridPane.columnIndex="0"
GridPane.rowIndex="{count(../preceding-sibling::question)}" />
</xsl:template>
<xsl:template name="controlCoords">
<GridPane.columnIndex>1</GridPane.columnIndex>
<GridPane.rowIndex>
<xsl:value-of select="count(preceding-sibling::question)"/>
</GridPane.rowIndex>
</xsl:template>
<xsl:template match="question[@type='desc']">
<TextArea fx:id="{@id}" id="{@id}">
<xsl:call-template name="controlCoords" />
</TextArea>
</xsl:template>
<xsl:template match="question[@type='list']">
<ComboBox fx:id="{@id}" id="{@id}">
<xsl:call-template name="controlCoords" />
<items>
<FXCollections fx:factory="observableArrayList">
<xsl:for-each select="choices/choice">
<String fx:value="{.}"/>
</xsl:for-each>
</FXCollections>
</items>
</ComboBox>
</xsl:template>
<xsl:template match="question[@type='value']">
<TextField fx:id="{@id}" id="{@id}">
<xsl:call-template name="controlCoords" />
</TextField>
</xsl:template>
<xsl:template match="question[@type='yesNo']">
<CheckBox fx:id="{@id}" id="{@id}">
<xsl:call-template name="controlCoords" />
</CheckBox>
</xsl:template>
</xsl:stylesheet>
지금 당신은 그냥 xsl
파일에서 Transformer
을 작성해야합니다 (I는 xml2fxml.xsl
이름). transform
메서드는 xml
파일을 읽고 xsl
파일의 규칙에 따라 변환 한 다음 출력을 출력 스트림으로 보냅니다. 당신은 그것에서 생성 된 fxml
을 읽을 수 및 입력 스트림에 FXMLLoader
지시 파이프에 약간의 속임수가 필요합니다
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
final PipedOutputStream transformOutput = new PipedOutputStream();
final PipedInputStream fxmlInputStream = new PipedInputStream(transformOutput);
Thread transformThread = new Thread(() -> {
try {
StreamSource xsltSource = new StreamSource(getClass().getResourceAsStream("xml2fxml.xsl"));
Transformer transformer = TransformerFactory.newInstance().newTransformer(xsltSource);
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StreamSource xmlSource = new StreamSource(getClass().getResourceAsStream("questionnaire.xml"));
StreamResult transformerResult = new StreamResult(transformOutput);
transformer.transform(xmlSource, transformerResult);
transformOutput.close();
} catch (Exception e) {
e.printStackTrace();
}
});
transformThread.start();
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(fxmlInputStream);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
업데이트 :이
(또한 위의 xsl
에 약간의 업데이트를 참고) 이것은 매우 매끄러운 반면 컨트롤러의 폼 컨트롤에 액세스하기가 어렵다는 점에서 너무 투명합니다. 올바른 요소를 찾으려면 FXML 정의 장면 그래프의 루트 요소 내용을 추악한 방식으로 검사해야합니다.
이 예제에서는 값을 얻기 위해 일부 리플렉션을 사용합니다. 당신은 또한 instanceof
테스트와 캐스팅을 많이 할 수 있습니다. 그것은 또한 그들이 "열심히"보기와 컨트롤러의 분리를 위반하는 1 열에 있다는 "알기"를 통해 컨트롤을 얻습니다. (Label
과 구별되는) 컨트롤에 할당 된 id
에 대한 규칙을 가지고 대신 사용하는 것이 좋습니다.
package xml2fx;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.control.SelectionModel;
import javafx.scene.control.TextInputControl;
import javafx.scene.layout.GridPane;
public class FormController {
private static final String SELECTED_VALUE = "yes" ;
private static final String UNSELECTED_VALUE = "no" ;
@FXML
private GridPane form ;
private final Map<String, Control> controls = new HashMap<>();
private final List<String> ids = new ArrayList<>();
public void initialize() {
for (Node node : form.getChildren()) {
if (GridPane.getColumnIndex(node) == 1) { // all form controls are in column 1
if (node instanceof Control) {
String id = node.getId();
controls.put(id, (Control)node);
ids.add(id);
}
}
}
}
public List<String> getIds() {
return Collections.unmodifiableList(ids);
}
public String getUserValue(String id) throws ReflectiveOperationException {
Control control = controls.get(id);
if (control == null) throw new IllegalArgumentException("No control with id "+id);
return getValueForControl(control);
}
private String getValueForControl(Control control) throws ReflectiveOperationException {
if (isTextControl(control)) {
return getTextControlValue(control);
} else if (isSelectable(control)) {
return getSelectableValue(control);
} else if (hasSelectionModel(control)) {
return getSelectedValue(control);
}
throw new IllegalArgumentException("Unsupported control class: "+control.getClass().getName());
}
private boolean isTextControl(Control control) {
// TextAreas, TextFields, etc:
return control instanceof TextInputControl ;
}
private String getTextControlValue(Control control) {
return ((TextInputControl) control).getText();
}
private boolean isSelectable(Control control) {
// ToggleButtons, CheckBoxes...
for (Method method : control.getClass().getMethods()) {
if (method.getName().equals("isSelected")
&& method.getReturnType() == boolean.class) {
return true ;
}
}
return false ;
}
private String getSelectableValue(Control control) throws ReflectiveOperationException {
Method isSelectedMethod = control.getClass().getMethod("isSelected");
boolean selected = (Boolean) isSelectedMethod.invoke(control);
if (selected) {
return SELECTED_VALUE ;
} else {
return UNSELECTED_VALUE ;
}
}
private boolean hasSelectionModel(Control control) {
// ComboBoxes, ListViews, TableViews, etc:
for (Method method : control.getClass().getMethods()) {
if (method.getName().equals("getSelectionModel")) {
return true ;
}
}
return false ;
}
private String getSelectedValue(Control control) throws ReflectiveOperationException {
Method selectionModelMethod = control.getClass().getMethod("getSelectionModel");
SelectionModel<?> selectionModel = (SelectionModel<?>) selectionModelMethod.invoke(control);
Object selectedItem = selectionModel.getSelectedItem();
if (selectedItem == null) {
return "" ;
} else {
return selectedItem.toString();
}
}
}
+1 노력으로 나는 상자에서 무엇인가를보아야합니다! :) – ItachiUchiha
와 나는 새로운 무엇인가 배우게되었다. :) –
당신은 선생님이다 천재 :) 놀랄만한 해결. 그리고 상세한 설명을위한 엄지 손가락, 그것은 명확하게 추가 선명도를 가지고 왔습니다! – Napster