简体   繁体   English

如何在JavaFX中动态更新ListView单元?

[英]How to update ListView cells dynamically in JavaFX?

I have a large OvervableList<Website> that is obtained from calling mainApp.getWebsitData() . 我有一个很大的OvervableList<Website> ,可以通过调用mainApp.getWebsitData()

In this controller, I make 3 separate FilteredList<Websites> : keepData , deleteData and newData based on 2 boolean properties of Website : 在此控制器中,基于Website 2个布尔属性,我制作了3个单独的FilteredList<Websites>keepDatadeleteDatanewData

private final BooleanProperty keep;
private final BooleanProperty delete;

Those 3 filtered lists are displayed in 3 different ListView s. 这3个过滤的列表显示在3个不同的ListView

I have buttons next to each item in the newData list where I make either keep or delete true. 我在newData列表中的每个项目旁边都有按钮,在其中我可以keep true或delete true。 Doing so should move that website object to either the keep list or delete list. 这样做应将网站对象移至保留列表或删除列表。 However, when I click the button the list views are not updating appropriately. 但是,当我单击按钮时,列表视图未正确更新。

public class WebsiteOverviewController {


    @FXML
    private ListView<Website> deleteList;

    @FXML 
    private ListView<Website> keepList;

    @FXML
    private ListView<Website> newList;

    @FXML
    private JFXButton scanButton;

    @FXML
    private JFXButton pauseButton;

    @FXML
    private StackPane stackPane;

    private BooleanProperty isScanning = new SimpleBooleanProperty(false);

    private MainApp mainApp;

    private FilteredList<Website> keepData;

    private FilteredList<Website> deleteData;

    private FilteredList<Website> newData;

    private AtomicBoolean paused = new AtomicBoolean(false);

    private Thread thread;

    public WebsiteOverviewController() {

    }

    @FXML
    public void initialize() {
        scanButton.setContentDisplay(ContentDisplay.RIGHT);
    }

    public void setMainApp(MainApp mainApp) {
        this.mainApp = mainApp;

        keepData = new FilteredList<>(mainApp.getWebsiteData(), p -> (!p.getDelete() && p.getKeep()));
        deleteData = new FilteredList<>(mainApp.getWebsiteData(), p -> (p.getDelete() && !p.getKeep()));

        newData = new FilteredList<>(mainApp.getWebsiteData(), p -> (!p.getKeep() && !p.getDelete()));

        if (!deleteData.isEmpty()) {
            deleteList.setItems(deleteData);

            deleteList.setCellFactory(param -> new ListCell<Website>() {
                @Override
                protected void updateItem(Website item, boolean empty) {
                    super.updateItem(item,  empty);

                    if (empty || item == null) {
                        setText(null);
                    } else {
                        setText(item.getWebsite());
                    }
                }
            });
        }

        if (!keepData.isEmpty()) {
            keepList.setItems(keepData);

            keepList.setCellFactory(param -> new ListCell<Website>() {
                @Override
                protected void updateItem(Website item, boolean empty) {
                    super.updateItem(item,  empty);

                    if (empty || item == null) {
                        setText(null);
                    } else {
                        setText(item.getWebsite());
                    }
                }
            });
        }

        if (!newData.isEmpty()) {
            newList.setItems(newData);

            newList.setCellFactory(param -> new ListCell<Website>() {
                @Override
                protected void updateItem(Website item, boolean empty) {
                    super.updateItem(item, empty);

                    if (empty || item == null) {
                        setText(null);
                    } else {
                        HBox hBox = new HBox();
                        hBox.setAlignment(Pos.CENTER);
                        JFXButton keepBtn = new JFXButton("Keep");
                        JFXButton deleteBtn = new JFXButton("Delete");

                        Label label = new Label(item.getWebsite());
                        label.setAlignment(Pos.CENTER);

                        hBox.getChildren().addAll(keepBtn, label, deleteBtn);
                        setGraphic(hBox);

                        keepBtn.setOnAction(new EventHandler<ActionEvent>() {
                            @Override
                            public void handle(ActionEvent event) {
                                System.out.println(item.getWebsite());
                                item.setKeep(true);
                            }
                        });
                    }
                }
            });
        }
        scanButton.visibleProperty().bind(isScanning.not());
        pauseButton.visibleProperty().bind(isScanning);


    }

    @FXML
    public void handleScanInbox() {
        isScanning.set(true);

        if (thread == null) {
            thread = new Thread() {
                public void run() {
                    mainApp.handleScanInbox(paused);
                }
            };
            thread.setDaemon(true);
            thread.start();
        } else {
            synchronized (paused) {
                if (paused.get()) {
                    paused.set(false);
                    paused.notify();
                }
            }
        }

    }

    @FXML
    public void handlePauseScanInbox() {
        paused.compareAndSet(false,  true);
        isScanning.set(false);

    }



}

Any ideas on how to update the lists dynamically? 关于如何动态更新列表的任何想法?

For the FilteredList to be updated, the listener added to the ObservableList needs to be triggered. 为了更新FilteredList ,需要触发添加到ObservableList的侦听器。 Modifying a property of a list element won't do the trick by default. 默认情况下,修改列表元素的属性不会成功。

If you create the list using a extractor for element properties that should trigger update changes, the FilteredList updates too. 如果使用提取器创建应触发更新更改的元素属性的列表,则FilteredList也会更新。

Example

public class Item {
    private final int i;
    private final BooleanProperty keep = new SimpleBooleanProperty();

    public Item(int i) {
        this.i = i;
    }

    @Override
    public String toString() {
        return Integer.toString(i);
    }

    public BooleanProperty keepProperty() {
        return keep;
    }

    public boolean isKeep() {
        return keep.get();
    }

    public void setKeep(boolean value) {
        keep.set(value);
    }

}
ObservableList<Item> data = FXCollections.observableArrayList(new Callback<Item, Observable[]>() {

    @Override
    public Observable[] call(Item param) {
        return new Observable[] { param.keepProperty() };
    }

});
for (int i = 0; i < 10; i++) {
    data.add(new Item(i));
}
FilteredList<Item> filtered = new FilteredList<>(data, Item::isKeep);
filtered.addListener((Observable o) -> System.out.println(filtered));
data.get(3).setKeep(true);
data.get(6).setKeep(true);
data.get(7).setKeep(true);
data.get(3).setKeep(false);

Output 产量

[3]
[3, 6]
[3, 6, 7]
[6, 7]

Returning an array containing multiple Observable s triggers an update, if any of those Observable s is modified. 如果修改了其中的任何Observable ,则返回包含多个Observable的数组将触发更新。

I recommend double checking, if keep and delete are actually properties associated with the item type or if you simply added those properties to be able to partition your list into 3 FilteredList s. 我建议仔细检查,如果keepdelete实际上是与项目类型相关联的属性,或者只是添加了这些属性以将列表划分为3个FilteredList Would a database table containing the item contain columns for those values? 包含该项的数据库表中是否会包含这些值的列? Would you save them to a output file, if you had to save the items to a file? 如果必须将项目保存到文件中,是否会将它们保存到输出文件中?
If the answer to these questions is "No" , you should use 3 seperate ObservableList s. 如果对这些问题的回答为“否” ,则应使用3个单独的ObservableList Moving items from one list to another is less complicated than dealing with 2 properties and filtering the list based on those. 将项目从一个列表移动到另一个列表要比处理2个属性并根据这些属性过滤列表要复杂得多。

Why you use a BooleanProperty for this, is there any reason? 为什么为此使用BooleanProperty,有什么原因吗? Because you can just go with 3 List. 因为您可以只使用3 List。

You can go with 2 Options, you can make a Context Menu for the List View or a Button next to it, i am a fan of the Context Menu. 您可以使用2个选项,可以为列表视图创建一个上下文菜单,或者在其旁边创建一个按钮,我是上下文菜单的粉丝。

In FXML you can specify a setOnAction()-Method, this method can be used to transfer the Selection Model from one Node to one of your keep or delete Lists. 在FXML中,您可以指定setOnAction()-Method,此方法可用于将选择模型从一个Node传输到保留或删除列表之一。

After this everything should work fine. 在此之后,一切应该正常。

private ListView<String>list;
private ListView<String> deleteList;

@FXML
private final void deleteSetOnAction(ActionEvent event) {
    deleteList.getItems().addAll(list.getSelectionModel().getSelectedItems());
    list.getItems().removeAll(list.getSelectionModel().getSelectedItems());
}

The deleteSetOnActionMethod is specified for a button or Context Menu in this example. 在此示例中,为按钮或上下文菜单指定了deleteSetOnActionMethod。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM