2014-06-20 2 views
2

저는 JavaFX의 초보자입니다. 몇 가지 샘플을 둘러 보면서 우리가 개발하려고하는 응용 프로그램에서 제대로 작동하는지 확인하려고합니다. 우리 앱의 첫 번째 단계는 사용자가 많은 질문을 던지고 응답을 기록하는 일종의 데이터 입력 단계입니다. 여기서 다른 팀이 질문 집합을 작성하고 있으며 이러한 질문은 XML 형식으로되어 있습니다.즉석에서 JavaFX UI를 동적으로 구현하십시오.

<?xml version="1.0" encoding="UTF-8"?> 
<userData>  
<question id="Q1" type ="desc"> 
    <text>Enter the name of the Component</text> 
</question> 
<question id ="Q2" type ="list"> 
    <text>Select mechanism type</text> 
    <choices> 
     <choice> Type 1 </choice> 
     <choice> Type 2 </choice> 
     <choice> Type 3 </choice> 
     <choice> Type 4 </choice> 
    </choices> 
</question> 
<question id ="Q5" type="yesNo"> 
    <text> Whether the parts have been verified by the supervisor? </text> 
</question> 
<question id ="Q6" type="yesNo"> 
    <text> Whether the component is available within the domicile </text> 
</question> 
<question id ="Q7" type="value"> 
    <text> Enter the quantity </text> 
</question> 
<question id ="Q8" type="value"> 
    <text> Enter the unit price </text> 
</question> 
</userData> 

그것은 부울 라디오 버튼을 갖는 같은 다양한 분야에 해당하는 그 만약 YESNO 타입 목록 등 값위한 텍스트 필드의 경우에 드롭. 이 질문은 사용자에 따라 달라질 수 있으므로 사용자는이 파일을 통해 질문을 구성 할 수 있습니다.

아이디어는 응용 프로그램 시작 중에이 XML을로드하고 구문 분석 한 후 적절한 UI 구성 요소를 즉시 작성하는 것입니다. JavaFX를 통해이를 달성 할 수 있습니까? SceneBuilder를 통해 제작 된 FXML 파일을 사용하여이 애플리케이션의 작은 프로토 타입을 만들었습니다. 그러나 시작하는 동안로드 된 질문 XML 파일을 구문 분석 한 후에 프로그래밍 방식으로 쿼리 용 UI 구성 요소를 작성하는 데 필요한 FXML 파일을 생성하는 것이 트릭입니다.

이 기능을 수행하기에 좋은 시작점은 무엇입니까?

답변

10

여기에는 몇 가지 방법이 있습니다.

하나는 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(); 
     } 
    } 
} 
+0

+1 노력으로 나는 상자에서 무엇인가를보아야합니다! :) – ItachiUchiha

+0

와 나는 새로운 무엇인가 배우게되었다. :) –

+0

당신은 선생님이다 천재 :) 놀랄만한 해결. 그리고 상세한 설명을위한 엄지 손가락, 그것은 명확하게 추가 선명도를 가지고 왔습니다! – Napster

관련 문제