2017-01-11 1 views
0

Я создал пользовательский компонент TableBlock. Он состоит из Label и TableView. TableViewcan может иметь, например, от 1 до 1000 строк. Количество строк определяется параметром «rowsFromPrefs» в файле FXML. Этот параметр необходим для создания TableView. TableView полностью создается кодом JAva, в fxml - только его тег и параметр с количеством строк.Задайте настраиваемые свойства FXML в качестве параметров для настраиваемого компонента javafx

Как я знаю, когда JavaFX создает компонент FXML, он сначала вызывает конструктор, затем @FXML аннотированные поля, а затем запускает метод initialize().

В моем случае при запуске initialize() переменные rowsFromPrefs по-прежнему равны нулю! Но, если я попытаюсь получить значение rowsFromPrefs из другого потока (а не JavaFX-launcher), я вижу, что он определен = «2», как и должно быть.

Так что я не могу понять, в какой момент Java назначает объектные параметры из файла FXML. Как передать параметр из файла fxml в объект, когда он создается.

Я видел аннотацию @NamedArg для параметров конструктора. Это единственный способ передачи параметра при создании объектов?

контроллер может определить метод Initialize(), который будет вызываться один раз> исполнителем контроллера, когда содержание связанного с ним документа, которые> были полностью загружены:

TableBlock.java

public class TableBlock extends VBox{ 
    @FXML 
    private String rowsFromPrefs; 
    @FXML 
    private Label label; 

public TableBlock() { 
    FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("TableBlock.fxml")); 
    fxmlLoader.setRoot(this); 
    fxmlLoader.setController(this); 
    try { 
     fxmlLoader.load(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

@FXML 
public void initialize() { 
    this.table = createTable(rowsFromPrefs); 
} 

public String getRowsFromPrefs() { 
    System.out.println("getRowsFromPrefs"); 
    return rowsFromPrefs; 
} 


public void setRowsFromPrefs(String rowsFromPrefs) { 
    this.rowsFromPrefs = rowsFromPrefs; 
} 

}

TableBlock.fxml

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

<?import javafx.scene.control.*?> 
<?import ru.laz.model.controls.*?> 
<?import java.lang.*?> 
<?import javafx.scene.layout.*?> 
<?import javafx.scene.layout.AnchorPane?> 
<?import ru.laz.model.controls.tableblock.*?> 


<fx:root type="javafx.scene.layout.VBox" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> 
    <children> 
     <Label text="Label" /> 
    </children> 
</fx:root> 

View.java

public class View extends Application { 
Parent root = null; 
private Scene scene; 

@Override 
    public void init() { 
    try { 
      root = FXMLLoader.load(getClass().getResource("View.fxml")); 
      root.requestLayout(); 
     } catch (IOException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
} 

@Override 
    public void start(final Stage stage) throws Exception { 
    scene = new Scene(root, 640, 480, Color.LIGHTGRAY); 
    stage.show(); 
} 

    public static void main(String[] args) { 
     launch(args); 
    } 

} 

View.fxml

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

<?import java.lang.*?> 
<?import javafx.scene.layout.*?> 
<?import javafx.scene.control.*?> 
<?import ru.laz.model.controls.tableblock.*?> 


<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> 
    <children> 
     <TableBlock rowsFromPrefs="2" id="IDDQD"/> 
    </children> 
</AnchorPane> 

ответ

1

Во-первых, обратите внимание, что @FXML аннотацию на rowsFromPrefs не служит никакой цели. @FXML вызывает значение для поля, когда файл FXML, для которого текущий объект является контроллером, имеет элемент с атрибутом fx:id, значение которого соответствует имени поля. Поскольку TableBlock.fxml не имеет элемента с fx:id="rowsFromPrefs", эта аннотация ничего не делает.

Когда FXMLLoader что загружается View.fxml встречает <TableBlock> элемент, он создает экземпляр TableBlock путем вызова его конструктора. Затем он установит значения, указанные атрибутами. Так что ваш FXML элемент

<TableBlock rowsFromPrefs="2" id="IDDQD"/> 

по существу эквивалентно

TableBlock tableBlock = new TableBlock(); 
tableBlock.setRowsFromPrefs("2"); 
tableBlock.setId("IDDQD"); 

Конечно, конструктор TableBlock просто делает то, что код говорит делать: он создает FXMLLoader, устанавливает корень и контроллер для этого FXMLLoader, а затем звонит load().Процесс загрузки для , которыйFXMLLoader установит поля ввода @FXML на контроллер (объект, который выполняет конструктор TableBlock), а затем вызывает initialize().

Так что initialize() вызывается как часть вызова FXMLLoader.load(), который находится в конструкторе TableBlock; конечно, все это происходит до того, как вызывается setRowsFromPrefs("2");.

Таким образом, в итоге, TableBlock.initialize() вызывается после TableBlock.fxml был проанализирован, и любые элементы, определенные там вводили в их соответствующих @FXML -annotated полей, но это происходит прежде, чем был загружен View.fxml.

Один из способов исправить это, чтобы передать rowsFromPrefs в конструктор TableBlock. Чтобы сделать это, используйте @NamedArg annotation:

public class TableBlock extends VBox{ 

    private final String rowsFromPrefs; 

    @FXML 
    private Label label; 

    public TableBlock(@NamedArg("rowsFromPrefs") String rowsFromPrefs) { 

     this.rowsFromPrefs = rowsFromPrefs ; 
     FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("TableBlock.fxml")); 
     fxmlLoader.setRoot(this); 
     fxmlLoader.setController(this); 
     try { 
      fxmlLoader.load(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

    @FXML 
    public void initialize() { 
     this.table = createTable(rowsFromPrefs); 
    } 

    public String getRowsFromPrefs() { 
     System.out.println("getRowsFromPrefs"); 
     return rowsFromPrefs; 
    } 


} 

Теперь ваш атрибут в FXML будет передан в конструктор вместо к способу набора, так rowsFromPrefs будет инициализирован перед вызовом fxmlLoader.load(), как это требуется.

Другим вариантом, конечно же, было бы просто переместить код из метода initialize() в метод setRowsFromPrefs(...). Я бы использовал описанную выше опцию, если вы намерены установить rowsFromPrefs для каждого экземпляра TableBlock и использовать второй вариант, только если вы хотите иметь возможность изменять rowsFromBlocks в течение жизненного цикла отдельного экземпляра TableBlock.

+0

большое спасибо для подробного объяснения. NamedArg - это подходящее решение, как я думаю. – LazerJet

Смежные вопросы