JavaFX 使用自定义对象编辑 TreeView

JavaFX Editing TreeView with custom object

我需要使 TreeView 可编辑,并且 TreeView 具有通用类型的 TreeItem Content。 我通过覆盖 Content class 中的 toString() 设法在 TreeView 上显示我的对象。但是,即使我知道自定义编辑功能与 CellFactory 有关系,我还是无法使用它。

我想要实现的编辑如下所示:

  1. 当我通过双击或按 Enter 键开始编辑单元格时,会显示一个 TextField。
  2. 如果用户完成编辑后单元格中有':'分隔符,则将前者和后者分别保存在namedescription中 class Property 扩展 Content.
  3. 如果没有,在扩展Content.
  4. 的classConcept中保存name的内容

MainController.java

package jsh.hiercards;

import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTreeView;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.scene.input.KeyEvent;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {

    @FXML
    public JFXTreeView<Content> treeView;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        TreeItem<Content> root = new TreeItem<>(new Concept("TESTROOT", null));
        root.getChildren().add(new TreeItem<>(new Concept("TESTCONCEPT", (Concept) root.getValue())));
        root.getChildren().add(new TreeItem<>(new Property("TESTPROPERTY", (Concept) root.getValue(), "DESCRIPTION")));
        treeView.setRoot(root);
    }

    @FXML
    public void startEdit(TreeView.EditEvent e) {
        System.out.println("Start");
    }

    @FXML
    public void commitEdit(TreeView.EditEvent e) {
        System.out.println("Commit");
    }

    @FXML
    public void cancelEdit(TreeView.EditEvent e) {
        System.out.println("Cancel");
    }

    @FXML
    public void typeHandle(KeyEvent e) {
        System.out.println("Type");
    }
}

Content.java

package jsh.hiercards;

import javafx.scene.control.TreeItem;

public abstract class Content {

    public String name;
    public Concept parent;

    private Content() {
    }

    public Content(String name, Concept parent) {
        this.name = name;
        this.parent = parent;
    }

    public abstract String toString();
}

Concept.java

package jsh.hiercards;

import javafx.scene.control.TreeItem;

public class Concept extends Content {

    public Concept(String name, Concept parent) {
        super(name, parent);
    }

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

Property.java

package jsh.hiercards;

import javafx.scene.control.TreeItem;

public class Property extends Content {

    public String description;

    public Property(String name, Concept parent, String description) {
        super(name, parent);
        this.description = description;
    }

    @Override
    public String toString() {
        return name + " : " + description;
    }
}

我如何对它们进行编码以创建有效的编辑功能?

-- 抱歉英语不好,我是留学生:(

以下应该适用于您的控制器。如果您使用 TextFieldTreeCell,则无需自己处理编辑事件。

使用 toString() 来确定对象在 UI 中的显示方式也不是好的做法(它使用本质上实现部分视图的代码“污染”了数据模型;您可能并且很可能会希望 toString() 方法提供不同的信息,例如用于调试)。

所以像这样:

package jsh.hiercards;

public abstract class Content {

    private String name;
    private Concept parent;

    private Content() {
    }

    public Content(String name, Concept parent) {
        this.name = name;
        this.parent = parent;
    }

    public String getName() {
        return name ;
    }

    public Concept getParent() {
        return parent ;
    }

}
package jsh.hiercards;

public class Concept extends Content {

    public Concept(String name, Concept parent) {
        super(name, parent);
    }

}
package jsh.hiercards;
    
public class Property extends Content {

    private String description;

    public Property(String name, Concept parent, String description) {
        super(name, parent);
        this.description = description;
    }

    public String getDescription() {
        return description ;
    }
}
package jsh.hiercards;

import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTreeView;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.scene.input.KeyEvent;
import javafx.util.StringConverter ;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {

    @FXML
    public JFXTreeView<Content> treeView;

    @Override
    public void initialize(URL location, ResourceBundle resources) {

        treeView.setCellFactory(tv -> new TextFieldTreeCell<>(new StringConverter<>() {
            @Override
            public Content fromString(String text) {
                TreeItem<Content> parentItem = getTreeItem().getParent();
                Content parent = parentItem == null ? null : parentItem.getValue() ;
                String[] tokens = text.split(":", 2);
                if (tokens.length < 2) {
                    return new Concept(tokens[0], parent);
                } else return new Property(tokens[0], parent, tokens[1]);
            }

            @Override
            public String toString(Content content) {
                if (content instanceof Property property) {
                    return property.getName() + " : " + property.getDescription();
                }
                return content.getName();
            }
        }));

        TreeItem<Content> root = new TreeItem<>(new Concept("TESTROOT", null));

        root.getChildren().add(new TreeItem<>(new Concept("TESTCONCEPT", (Concept) root.getValue())));
        root.getChildren().add(new TreeItem<>(new Property("TESTPROPERTY", (Concept) root.getValue(), "DESCRIPTION")));
        treeView.setRoot(root);
    }

    /*
    @FXML
    public void startEdit(TreeView.EditEvent e) {
        System.out.println("Start");
    }

    @FXML
    public void commitEdit(TreeView.EditEvent e) {
        System.out.println("Commit");
    }

    @FXML
    public void cancelEdit(TreeView.EditEvent e) {
        System.out.println("Cancel");
    }
    */

    @FXML
    public void typeHandle(KeyEvent e) {
        System.out.println("Type");
    }
}