gpt4 book ai didi

treeview - JavaFX - TreeView 在删除所选项目时的奇怪行为

转载 作者:行者123 更新时间:2023-12-02 07:31:02 28 4
gpt4 key购买 nike

在我的应用程序中,左侧有一个 TreeView,我根据 TreeView 中的选择更新右侧的 Pane 。一个非常直接的场景。当选择为空时,我会在 Pane 中显示一条类似“请进行选择”的消息,即我还TreeView中处理空选择

在应用程序的生命周期内,可以向TreeView添加或删除一些项目。当 TreeView 中的所选项目被删除时,我遇到了一些问题。在本例中,我希望 TreeView 的选择变为 null,但事实并非如此!

为了调试这个案例,我编写了一个简单的 FXML 应用程序,如下所示:

FXMLDocument.fxml:

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" prefHeight="350.0" prefWidth="250.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="treeviewbug.FXMLDocumentController">
<children>
<TreeView fx:id="treeView" prefHeight="350.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="50.0" />
<Button mnemonicParsing="false" onAction="#update" text="update" AnchorPane.leftAnchor="20.0" AnchorPane.topAnchor="15.0" />
<Label fx:id="selectionLabel" text="" AnchorPane.leftAnchor="100.0" AnchorPane.rightAnchor="20.0" AnchorPane.topAnchor="20.0" />
</children>
</AnchorPane>

FXMLDocumentController.java:

package treeviewbug;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.HBox;
import javafx.util.Callback;

public class FXMLDocumentController implements Initializable {

@FXML
Label selectionLabel;
@FXML
private TreeView<String> treeView;
private TreeItem<String> selectedItem = null;
private ChangeListener<String> changeListener = new ChangeListener<String>() {

@Override
public void changed(ObservableValue<? extends String> ov, String oldValue, String newValue) {
selectionLabel.setText(newValue);
}
};

@Override
public void initialize(URL url, ResourceBundle rb) {
final TreeItem<String> root = new TreeItem<>();
for (int i = 1; i <= 20; i++) {
root.getChildren().add(new TreeItem<String>("Item " + i));
}
treeView.setShowRoot(false);
treeView.setRoot(root);
treeView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TreeItem<String>>() {

@Override
public synchronized void changed(ObservableValue<? extends TreeItem<String>> ov, TreeItem<String> oldSelection, TreeItem<String> newSelection) {
if (selectedItem != null) {
selectedItem.valueProperty().removeListener(changeListener);
}
if (newSelection == null) {
selectionLabel.setText("selection is null");
} else {
selectionLabel.setText(newSelection.getValue());
}
selectedItem = newSelection;
if (selectedItem != null) {
selectedItem.valueProperty().addListener(changeListener);
}
}
});
treeView.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {

@Override
public TreeCell<String> call(TreeView<String> p) {
System.out.println("Creating new cell.");
return new TreeCell<String>() {

Label label = new Label();
Button button = new Button("remove");
HBox box = new HBox(20);
{
button.setOnAction(new EventHandler<ActionEvent>() {

@Override
public void handle(ActionEvent t) {
TreeItem<String> itemToRemove = null;
for (TreeItem<String> item : root.getChildren()) {
if (item.getValue().equals(getItem())) {
itemToRemove = item;
break;
}
}
if (itemToRemove != null) {
root.getChildren().remove(itemToRemove);
}
t.consume();
}
});
box.getChildren().addAll(label, button);
}

@Override
protected void updateItem(String value, boolean bln) {
super.updateItem(value, bln);
if (value != null) {
label.setText(value);
setGraphic(box);
} else {
setGraphic(null);
}
}
};
}
});
}

@FXML
private void update() {
TreeItem<String> i = treeView.getSelectionModel().getSelectedItem();
if (i == null) {
selectionLabel.setText("selection is null");
} else {
selectionLabel.setText(i.getValue());
}
}

}

最初,TreeView 填充了 20 个项目。如果用户单击某个 TreeItem,则会将监听器附加到 selectedItemProperty。监听器还会附加到所选 TreeItemvalueProperty,以防用户未单击某个项目,但所选项目的值由于某种原因发生更改。用户可以通过单击特定项目中的删除按钮来删除该项目。用户可以随时单击更新按钮来更新显示当前选择的标签内容,以防我的处理程序错过某些事件。

在这个简单的测试应用程序中,当我删除选定的项目时,它有时将删除的项目之前的项目显示为选定的项目,有时将删除的项目之后的项目显示为选定的项目。 但是,所选项目大多数时候不会更改,即使它不再包含在 TreeView 中!

我的第一个问题是,这是正常现象还是错误?你见过这样的事情吗?

作为解决方法,我在 for 循环之后添加了以下代码:

root.getChildren().addListener(new ListChangeListener<TreeItem<String>>() {

@Override
public void onChanged(ListChangeListener.Change<? extends TreeItem<String>> change) {
while (change.next()) {
if (change.wasRemoved() && selectedItem != null) {
if (change.getRemoved().contains(selectedItem)) {
selectedItem.valueProperty().removeListener(changeListener);
selectedItem = null;
treeView.getSelectionModel().clearSelection();
}
}
}
}
});

现在,不存在像选择不存在的项目这样的奇怪情况了。但有时选择不为空,尽管我调用 clearSelection()。我的第二个问题是,这种自动选择正常吗?

最后一个问题,有更好的解决方法吗?

抱歉,这是一个很长的问题。如果您仍在阅读,谢谢您:)

最佳答案

在 Java 1.8.0_92 中,行为似乎是:

  • 如果选择后面的项目被删除 selectedItem 和 selectedIndex 保持不变,
  • 如果删除选择之前的某个项目 selectedItem 保持不变,但 selectedIndex 发生变化(因为 selectedItem 更改了其位置),
  • 如果所选项目被删除,selectedIndex 会递减,selectedItem 也会发生类似的变化;仅当第一项被删除时 selectedIndex 保持不变。

如果删除最后一项,就会发生唯一奇怪的行为。在这种情况下,selectedIndex 和 selectedItem 不会改变,正如 Gman 所说的那样。这会导致选择不再存在的项目(似乎是一个错误)。

作为解决方法,您可以检查删除代码中的根子级。

删除按钮的 onAction 可能如下所示:

button.setOnAction(t -> {
TreeItem<String> item = getTreeItem();
item.getParent().getChildren().remove(item); // remove item
if(treeView.getRoot().getChildren().size() == 0) { // check for empty tree
treeView.getSelectionModel().clearSelection();
}
t.consume();
});

关于treeview - JavaFX - TreeView 在删除所选项目时的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21911773/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com