Reputation: 20555
I'm trying to create a menu using a treeView. This is the first time I'm using treeView and have been reading up on it on several websites.
I'm having some problems when it comes to action event. What I want to do is basically to fire and event when ever the user clicks a node in the treeview so far I have the following:
TreeItem<String> rootItem = new TreeItem<String>("Navigation");
TreeItem<String> statistics = new TreeItem<String>("Statistics");
TreeItem<String> clan = new TreeItem<String>("Clan page");
clan.addEventHandler(MouseEvent, new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
// TODO Auto-generated method stub
}
});
rootItem.getChildren().add(statistics);
rootItem.getChildren().add(clan);
TreeView<String> tree = new TreeView<String>(rootItem);
Sadly this doesn't seem to work.
Is there any way I can add a clicklistener
or even an actionlistener
to the individual items in my treeView
without changing the treeItems
to type Button
?
Upvotes: 5
Views: 30356
Reputation: 2523
I actually accomplished this in a round-about way, though it is extremely flexible, and even allows you to have TreeItems that engage different classes while still being able to nest them within each other.
It can be rather daunting to look at upon initial inspection, but if you follow it closely, it will make perfect sense.
You start with your class containing the TreeView - we'll call it TreeViewClass.
This example came from a program I'm working on that interacts with GitHub Gists, which explains the various objects that I interact with, in the classes.
public class TreeViewClass {
public void start() {
TextArea itemInfo = new TextArea();
SplitPane splitPane = new SplitPane(getTreeView(), itemInfo);
splitPane.setDividerPosition(0, .1);
Stage stage = new Stage();
stage.setScene(new Scene(splitPane));
stage.show();
}
private Collection<TreeItem<MyTreeItem>> gistObjectTreeItems() {
Collection<TreeItem<MyTreeItem>> gistTreeItemObjects = FXCollections.observableArrayList();
List<GistObject> gistObjects = GitHubApi.getGistObjectList();
for (GistObject gistObject : gistObjects) {
MyTreeItem myGistObjectTreeItem = new MyTreeItem(gistObject);
TreeItem<MyTreeItem> objectTreeItem = new TreeItem<>(myGistObjectTreeItem);
List<GistFile> gistFileList = gistObject.getFileList();
for(GistFile file : gistFileList) {
TreeItem<MyTreeItem> fileTreeItem = new TreeItem<>(new MyTreeItem(file));
objectTreeItem.getChildren().add(fileTreeItem);
}
gistTreeItemObjects.add(gistObjectTreeItem);
}
return gistTreeItemObjects;
}
/**
We are returning a Collection of TreeItems, each one contains an instance
of MyTreeItem using the constructor that defines the instance as a GistObject.
We then add to each of those objects, more TreeItems that contain the same
class MyTreeItem, only we use the constructor that sets the instance as
GistFile, and obviously, each Gist can have many GistFiles.
**/
private TreeView<MyTreeItem> getTreeView() {
TreeView<MyTreeItem> treeView = new TreeObject(this);
TreeItem<MyTreeItem> treeRoot = new TreeItem<>(new MyTreeItem());
treeRoot.getChildren().addAll(gistObjectTreeItems());
treeView.setRoot(treeRoot);
treeView.setShowRoot(false);
return treeView;
}
/**
getTreeView merely builds the TreeView, then adds to it, the Collection
of MyTreeItems that contain the GistObject and each of those objects
GistFiles. We need a root in the tree, so we add one that also contains
the MyTreeItem class only we use the default constructor, since it is
neither a GistObject or a GistFile, and then we hide it because we
don't need to interact with it.
**/
}
This is the main TreeView extended class:
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.input.MouseEvent;
public class TreeObject extends TreeView<MyTreeItem> {
public TreeObject() {
super();
addEventHandler(MouseEvent.MOUSE_CLICKED, e->{
if(e.getClickCount() == 1) {
selected = getSelectionModel().getSelectedItem();
if (selected != null) {
gistFile = selected.getValue().getFile();
gist = selected.getValue().getGist();
if (gistFile != null) {
GitHubApi.setSelected(gistFile);
}
else if (gist != null) {
GitHubApi.setSelected(gist);
}
}
}
});
}
TreeItem<MyTreeItem> selected;
GistFile gistFile;
GistObject gist;
}
/**
This is the class that gives us the ability to add all manner of events that we might
want to look for. In this case, I am looking for MOUSE_CLICKED events and notice I can
even check to see how many mouse clicks the user enacted on the TreeItems. When the user
clicks on the TreeItem, the selected object returns the MyTreeItem instance that
is assigned to whichever TreeItem they click on, I can then test to see which way
MyTreeItem has been configured (GistObject or GistFile) and act accordingly.
**/
Finally, we have the MyTreeItem class:
public class MyTreeItem {
private enum TYPE {
GIST, FILE
}
public MyTreeItem(GistObject gist) {
this.gist = gist;
this.type = TYPE.GIST;
}
public MyTreeItem(GistFile file) {
this.file = file;
this.type = TYPE.FILE;
}
public MyTreeItem(){}
private TYPE type;
private GistObject gist;
private GistFile file;
public boolean isGist() { return type == TYPE.GIST; }
public boolean isFile() { return type == TYPE.FILE; }
public GistObject getGist() { return gist; }
public GistFile getFile() { return file; }
@Override
public String toString() {
if(this.type == TYPE.FILE) return StringUtils.truncate(file.toString(),25);
else if(this.type == TYPE.GIST) return StringUtils.truncate(gist.toString(),25);
return "";
}
}
/**
This should be fairly self-explanatory. This is the MyTreeItem class that can be instantiated
as either a GistObject or a GistFile. Note the toString returns different Strings depending
on how the class was instantiated. The toString() will automatically populate the label
on the TreeItem object that the class is assigned to.
**/
Upvotes: -1
Reputation: 309
treeView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TreeItem<String>>() {
@Override
public void changed(ObservableValue<? extends TreeItem<String>> observable,
TreeItem<String> oldValue, TreeItem<String> newValue) {
// newValue represents the selected itemTree
}
});
Upvotes: 2
Reputation: 26405
According to the JavaFX 2.2 documentation :
" ..a TreeItem is not a Node, and therefore no visual events will be fired on the TreeItem, To get these events, it is necessary to add relevant observers to the TreeCell instances (via a custom cell factory)."
I think this example on using TreeView
will be somehow useful.
Upvotes: 6
Reputation: 139
I couldn't find method getPickResult in mouse event, so maybe next is preferable then answer from Alex:
1) add listener to tree view
treeView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> handle(newValue));
2) handle clicks, it's not need distinguish clicks on empty space and nodes
private void updateSelectedItem(Object newValue) {
System.out.println(newValue);
}
Upvotes: 10
Reputation: 531
This may be solved by implementing CellFactory, but I think the easiest way is like this:
1) Create and add an event handler to the TreeView:
EventHandler<MouseEvent> mouseEventHandle = (MouseEvent event) -> {
handleMouseClicked(event);
};
treeView.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseEventHandle);
2) Handle only clicks on the nodes (and not on empy space os the TreeView):
private void handleMouseClicked(MouseEvent event) {
Node node = event.getPickResult().getIntersectedNode();
// Accept clicks only on node cells, and not on empty spaces of the TreeView
if (node instanceof Text || (node instanceof TreeCell && ((TreeCell) node).getText() != null)) {
String name = (String) ((TreeItem)treeView.getSelectionModel().getSelectedItem()).getValue();
System.out.println("Node click: " + name);
}
}
Upvotes: 16