QueN
QueN

Reputation: 163

JavaFX Editing TreeView with custom object

I need to make TreeView editable, and the TreeView has TreeItem with a generic type Content. I managed to display my objects on TreeView by overriding toString() in the Content class. However, even though I know the custom editing feature has something to do with CellFactory, I couldn't get it to work.

The editing I wanted to implement works like below:

  1. When I start editing a cell by double-clicking or press Enter, a TextField shows.
  2. If it has ':' seperator in the cell when a user has done editing, save the former and latter respectively in name and description in the class Property which extends Content.
  3. If not, save a content in name in the class Concept which extends Content.

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;
    }
}

How can I code them to make a editing feature which works?

-- Sorry for poor English, I'm a foreign student :(

Upvotes: 1

Views: 985

Answers (1)

James_D
James_D

Reputation: 209319

The following should work for your controller. If you use a TextFieldTreeCell there is no need to handle the edit events yourself.

It's also not good practice to use toString() to determine how an object is displayed in the UI (it "pollutes" the data model with code that is essentially implementing part of the view; you may, and probably will, want the toString() method to provide different information, e.g. for debugging).

So something like:

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");
    }
}

Upvotes: 2

Related Questions