JavaFX将一个组件添加到列表中,动态地更改它的内容

时间:2017-10-19 11:57:00

标签: java javafx fxml

使用Fxml,我的主屏幕上有一个ListView。

然后我做了一个单独的Fxml。这被称为卡片并显示金融股票信息(股票代码和当前价格有2个标签)

我如何创建卡的多个实例,以便它们各自具有不同的股票名称和价格,并且价格可以更新?

public ListView searchList;
public Button searchButton;

@FXML
public void handleSearchButton(ActionEvent event) throws IOException {
    loadFxml();
}

public void loadFxml () throws IOException {
    Pane newLoadedPane = FXMLLoader.load(getClass().getResource("card.fxml"));
    searchList.getItems().add(newLoadedPane); 
}  

目前,该代码会将卡的硬编码版本添加到ListView。而且我相信它们都是一样的,所以如果我要更新文本,它们都会更新。

1 个答案:

答案 0 :(得分:2)

要创建FXML定义的UI组件的多个实例,只需多次加载FXML文件即可。如果希望每个数据都有不同的数据,请在控制器中定义方法以更改数据。那么你基本上可以做到

for (int i = 0 ; i < numberOfStocks ; i++) {
    FXMLLoader loader = new FXMLLoader(getClass().getResource("card.fxml"));
    Pane stockView = loader.load();
    StockController controller = loader.getController();
    controller.setXXX(...);
    // ...
}

我强烈建议不要使用UI节点填充ListView,如问题中的代码所示。 UI元素是非常昂贵的对象(它们有数百个属性,并且受CSS支持,可能需要解析等)。您所代表的数据非常简单:一个字符串和一个代表股票代码和价格的双精度数据。 ListView旨在高效,因为它只为可见数据创建UI元素,重用这些元素,例如,用户滚动它们。因此,您的ListView应仅在其支持列表中使用数据,而不是UI元素。

因此,创建一个简单的类来表示股票:

public class Stock {

    private final String symbol ; // doesn't change

    private final DoubleProperty price = new SimpleDoubleProperty() ; // mutable and observable

    public Stock(String symbol, double price) {
        this.symbol = symbol ;
        setPrice(price);
    }

    public DoubleProperty priceProperty() {
        return price ;
    }

    public final double getPrice() {
        return priceProperty().get();
    }

    public final void setPrice(double price) {
        priceProperty().set(price);
    }

    public String getSymbol() {
        return symbol ;
    }
}

Stock类使用JavaFX properties,以便以后轻松绑定它们(因此,如果价格发生变化,UI可以自动更新)。

现在您可以定义一个简单的FXML:

<!-- imports etc -->

<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.StockController"/>
    <Label fx:id="symbolLabel"/>
    <Label fx:id="priceLabel" />
</VBox>

带有控制器,可以实现标签与正在显示的库存的绑定:

public class StockController {

    private Stock stock ;

    @FXML
    private Label symbolLabel ;

    @FXML
    private Label priceLabel ;

    public void setStock(Stock stock) {
        this.stock = stock ;
        priceLabel.textProperty().unbind();
        if (stock == null) {
            symbolLabel.setText(null);
            priceLabel.setText(null);
        } else {
            symbolLabel.setText(stock.getSymbol());
            priceLabel.textProperty().bind(stock.priceProperty().asString("Price: $%.2f"));
        }
    }
}

现在您可以使用FXML创建ListView<Stock>并使用其cell factory显示每个股票:

public class MainController {

    @FXML
    private ListView<Stock> searchList ;

    public void initialize() {
        searchList.setCellFactory(lv -> new StockListCell());
    }

}
public class StockListCell extends ListCell<Stock> {

    private final Pane stockView ;
    private final StockController stockController ;

    public StockListCell() {
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("card.fxml"));
            stockView = loader.load();
            stockController = loader.getController();
            setGraphic(stockView);
            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
        } catch (IOException exc) {
            // IOException here is fatal:
            throw new UncheckedIOException(exc);
        }
    }

    @Override
    protected void updateItem(Stock item, boolean empty) {
        super.updateItem(item, empty);
        stockController.setStock(item);
    }
}

请注意,ListView不会创建多个StockListCell(每个可见单元格只有一个),因此可以执行相当繁重的工作(例如加载FXML)构造函数。相比之下,updateItem(...)方法可能会被频繁调用(例如,当用户滚动时),并且应该做最少的工作。在这里,我们只更新我们的UI显示的库存,这实际上只是更改了几个标签的文本。

最后,您可以按照预期使用Stock填充列表视图:

for (int i = 0 ; i < numberOfStocks ; i++) {
    String symbol = "Stock "+i ;
    double price = 1000*Math.random();
    Stock stock = new Stock(symbol, price) ;
    searchList.getItems().add(stock);
}