我有一个JavaFX,它显示2个单独的ListView,如下所示:
有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);
}
}
答案 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()方法。