单击JavaFX中的按钮后,如何保持ListView中最后选择的单元格处于选中状态?

时间:2018-12-12 04:44:55

标签: listview javafx

我有一个JavaFX,它显示2个单独的ListView,如下所示:

enter image description here

有2个按钮可将所选单元格移到右侧列表中。现在,如果我选择一个单元格,请在右侧说“ Amazon Web Services”,然后按“ <<保留列表”按钮,然后它将其移到左侧。

但是,如果现在我想将第二个项目“ Rita M. Powell”也移到“保存列表”,则必须选择该单元格并再次按“保存列表”按钮。

我想做的是即使在按下按钮后仍保持ListView的第一行处于选中状态,这样我就不必回来再次选择顶部的单元格了。

目前我在控制器中的操作方式是保留实例变量private ListServer selected;并在每次单击单元格时分配该变量:

deleteListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            selected = newValue;
        });

        keepListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            selected = newValue;
        });

然后2个按钮通过更改附加到它们的属性来处理这些单元格的移动:

    @FXML
    public void moveKeep() {
        if (selected != null) {
            selected.setKeep(true);
            wrapKeepList();
        }
    }

    @FXML
    public void moveDelete() {
        if (selected != null) {
            selected.setKeep(false);
            wrapDeleteList();
        }
    }

其余的控制器代码:

public class ListServerOverviewController {


    @FXML
    private JFXListView<ListServer> deleteListView;

    @FXML 
    private JFXListView<ListServer> keepListView;

    @FXML
    private JFXButton scanButton;

    @FXML
    private JFXButton pauseButton;

    @FXML
    private JFXButton moveDeleteButton;

    @FXML
    private JFXButton moveKeepbutton;

    private BooleanProperty isScanning = new SimpleBooleanProperty(false);

    private MainApp mainApp;

    private FilteredList<ListServer> keepList;

    private FilteredList<ListServer> deleteList;

    private AtomicBoolean paused = new AtomicBoolean(false);

    private Thread thread;

    private ListServer selected;


    public ListServerOverviewController() {

    }

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

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

        wrapDeleteList();
        wrapKeepList();

        scanButton.visibleProperty().bind(isScanning.not());
        pauseButton.visibleProperty().bind(isScanning);

        deleteListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            selected = newValue;
        });

        keepListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            selected = newValue;
        });
    }

    private void wrapKeepList() {
        keepList = new FilteredList<>(mainApp.getListServerList(), p -> p.getKeep());

        if (!keepList.isEmpty()) {
            keepListView.setItems(keepList);
            keepListView.setCellFactory(param -> new ListCell<ListServer>() {
                @Override
                protected void updateItem(ListServer item, boolean empty) {
                    super.updateItem(item,  empty);

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

    private void wrapDeleteList() {
        deleteList = new FilteredList<>(mainApp.getListServerList(), p -> !p.getKeep());

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

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

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

    @FXML
    public void moveKeep() {
        if (selected != null) {
            selected.setKeep(true);
            wrapKeepList();
        }
    }

    @FXML
    public void moveDelete() {
        if (selected != null) {
            selected.setKeep(false);
            wrapDeleteList();
        }
    }

    @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);

    }

}

2 个答案:

答案 0 :(得分:2)

使用侦听器将所选值放在字段中不是一个好主意。两个侦听器都使用相同的字段,因此最后的选择更改确定要移动的项目,而不是ListView的最后更改。此外,检索该值的成本很小,并且可以在使用该值的事件处理程序中轻松完成。

此外,不必在每次更改keep属性时都更新列表。在列表之间移动后,使用选择模型选择转移的项目:

public static class Item {
    private final String text;
    private final BooleanProperty keep = new SimpleBooleanProperty();

    public Item(String text) {
        this.text = text;
    }

    @Override
    public String toString() {
        return text;
    }

    public BooleanProperty keepProperty() {
        return keep;
    }

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

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

}

private static void move(ListView<Item> source, ListView<Item> target) {
    Item item = source.getSelectionModel().getSelectedItem();
    if (item != null) {
        item.setKeep(!item.isKeep());
        target.getSelectionModel().clearSelection();
        target.getSelectionModel().select(item);
    }
}

@Override
public void start(Stage primaryStage) {
    ObservableList<Item> data = FXCollections.observableArrayList(new Callback<Item, Observable[]>() {

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

    });

    FilteredList<Item> keep = data.filtered(item -> item.isKeep());
    FilteredList<Item> remove = data.filtered(item -> !item.isKeep());

    ListView<Item> leftList = new ListView<>(keep);
    ListView<Item> rightList = new ListView<>(remove);

    Button moveLeft = new Button("<<");
    moveLeft.setOnAction(evt -> move(rightList, leftList));

    Button moveRight = new Button(">>");
    moveRight.setOnAction(evt -> move(leftList, rightList));

    for (int i = 0; i < 40; i++) {
        data.add(new Item(Integer.toString(i)));
    }

    Scene scene = new Scene(new HBox(10, leftList, moveLeft, moveRight, rightList));
    primaryStage.setScene(scene);
    primaryStage.show();
}

如果使用Item进行比较时,如果可能会有不同的项目true产生equals,则应使用TransformationList的方法来查找要选择和执行的索引而是按索引选择:

private static void move(ListView<Item> source, ListView<Item> target) {
    FilteredList<Item> sourceList = (FilteredList<Item>) source.getItems();
    FilteredList<Item> targetList = (FilteredList<Item>) target.getItems();
    Item item = source.getSelectionModel().getSelectedItem();
    if (item != null) {
        int index = sourceList.getSourceIndex(source.getSelectionModel().getSelectedIndex());
        item.setKeep(!item.isKeep());
        index = targetList.getViewIndex(index);
        target.getSelectionModel().clearAndSelect(index);
    }
}

答案 1 :(得分:1)

我建议不要保留对“选定的”实例变量的选定项目的引用,而应在按下按钮时立即获得选定的项目。同样,在执行了必需的操作之后,您可以调用selectionModel的selectFirst()方法来选择第一个选项。像..

// I assume moving from delete to keep
@FXML
public void moveKeep() {
    if (deleteListView.getSelectionModel().getSelectedItem()!= null) {
        deleteListView.getSelectionModel().getSelectedItem().setKeep(true);
        wrapKeepList();
        wrapDeleteList();
        deleteListView.getSelectionModel().selectFirst();
    }
}

您也无需在wrapXXX方法中保留单元工厂的实现。您可以将单元格出厂设置移动到setMainApp()方法。