我如何将焦点侦听器放在 JavaFX 中的 Treecell 上?
How do i put a focus listener on a Treecell in JavaFX?
我正在构建一个应用程序,我需要在显示数据的窗格旁边有一个树视图。当有人在树视图中选择一个项目时,应用程序必须识别他们选择的内容并从数据库中寻找正确的数据,然后以我选择的格式呈现它。用户如何选择树视图中的项目(鼠标单击、Tab 键、箭头键等)一定无关紧要,只要当项目获得焦点时,就会触发一个方法将数据呈现给用户。
我通过以下方式使 仅鼠标点击 完美运行:
// Application thread method to build the tree map, used in the generateTree
// method.
public void treeBuilder(TreeMap<ModelSites, ArrayList<ModelPlants>> map) {
TreeMap<ModelSites, ArrayList<ModelPlants>> treeMap = map;
final TreeItemProperties<String, String> rootTreeItem = new TreeItemProperties<String, String>("EMT", null);
TreeItemProperties<String, Integer> site = null;
TreeItemProperties<String, Integer> plant = null;
for (Map.Entry<ModelSites, ArrayList<ModelPlants>> entry : treeMap.entrySet()) {
site = new TreeItemProperties<String, Integer>(entry.getKey().getLongName(), entry.getKey().getPrimaryKey());
rootTreeItem.getChildren().add(site);
if (site.getValue().equalsIgnoreCase("test item")) {
site.setExpanded(true);
}
for (int i = 0; i < entry.getValue().size(); i++) {
plant = new TreeItemProperties<String, Integer>(entry.getValue().get(i).getSitePlantId() + " " + entry.getValue().get(i).getShortName(), entry.getValue().get(i).getPrimaryKey());
site.getChildren().add(plant);
}
}
//Cell Factory is used to effectively turn the tree items into nodes, which they are not natively.
//This is necessary to have actions linked to the tree items (eg. double click an item to open an edit window).
emtTree.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {
@Override
public TreeCell<String> call(TreeView<String> param) {
FactoryTreeCell<String> cell = new FactoryTreeCell<String>();
cell.setOnMouseClicked(event -> {
if (!cell.isEmpty()) {
@SuppressWarnings("unchecked")
TreeItemProperties<String, Integer> treeItem = (TreeItemProperties<String, Integer>) cell.getTreeItem();
generateEquipmentPanes(treeItem.getPropertyValue());
}
});
return cell;
}
});
rootTreeItem.setExpanded(true);
emtTree.setRoot(rootTreeItem);
}
// Populate the main screen with all equipment items in the selected plant.
public void generateEquipmentPanes(int plantId) {
int plant = plantId;
Task<LinkedList<ModelEquipment>> task = new Task<LinkedList<ModelEquipment>>() {
@Override
public LinkedList<ModelEquipment> call() {
LinkedList<ModelEquipment> equipmentList = DAOEquipment.listEquipmentByPlant(plant);
return equipmentList;
}
};
// When list is built successfully, send the results back to the application
// thread to load the equipment panes in the GUI.
task.setOnSucceeded(e -> equipmentPaneBuilder(task.getValue()));
task.setOnFailed(e -> task.getException().printStackTrace());
task.setOnCancelled(null);
String methodName = new Object() {}.getClass().getEnclosingMethod().getName();
Thread thread = new Thread(task);
thread.setName(methodName);
//System.out.println("Thread ID: " + thread.getId() + ", Thread Name: " + thread.getName());
thread.setDaemon(true);
thread.start();
}
// Application thread method to build the equipment panes, used in the
// generateEquipmentPanes method.
public void equipmentPaneBuilder(LinkedList<ModelEquipment> list) {
LinkedList<ModelEquipment> equipmentList = list;
EquipmentPanels.getChildren().clear();
for (int i = 0; i < equipmentList.size(); i++) {
ModelEquipment item = equipmentList.get(i);
try {
PaneEquipment equipmentPane = new PaneEquipment();
equipmentPane.updateFields(item.getTechId(), item.getShortName(), item.getLongDesc()); equipmentPane.setId("equipPane" + i);
EquipmentPanels.getChildren().add(equipmentPane);
} catch (Exception e) {
e.printStackTrace();
}
}
}
我进行了大量的搜索,我有点想出了如何实现监听器而不是处理程序,因为这似乎是做我想做的事情的方法 - 将监听器放在 属性细胞。但是当用监听器替换事件处理程序时,就像下面的两个例子,我遇到了很多问题。
emtTree.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
@SuppressWarnings("unchecked")
TreeItemProperties<String, Integer> treeItem = (TreeItemProperties<String, Integer>) cell.getTreeItem();
generateEquipmentPanes(treeItem.getPropertyValue());
}
});
cell.focusedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (!cell.isEmpty()) {
@SuppressWarnings("unchecked")
TreeItemProperties<String, Integer> treeItem = (TreeItemProperties<String, Integer>) cell.getTreeItem();
generateEquipmentPanes(treeItem.getPropertyValue());
}
}
});
开始时,每次单击来自行 generateEquipmentPanes(treeItem.getPropertyValue());
的树项时,我都会得到 nullpointerexceptions
。其次,它往往会从错误的项目中选取数据,而不是我选择的项目。然后在点击几下之后它似乎完全崩溃并且除了提供更多 nullpointerexceptions
.
什么都不做。
据我所知,我认为问题在于侦听器相对于需要传递给方法 generateEquipmentPanes
的变量的位置。还有一些关于在某个时间点删除侦听器并稍后重新添加它们的内容。
我应该以某种方式将监听器放入细胞工厂吗?目前它看起来像这样:
import javafx.scene.control.TreeCell;
public class FactoryTreeCell<T> extends TreeCell<T> {
public FactoryTreeCell() {
}
/*
* The update item method simply displays the cells in place of the tree items (which disappear when setCellFactory is set.
* This can be used for many more things (such as customising appearance) not implemented here.
*/
@Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null); //Note that a graphic can be many things, not just an image. Refer openJFX website for more details.
} else {
setText(item.toString());
}
}
}
我注意到的另一件事是还有其他方法可以实现 Callback
,这可能更适合听众,但我不知道该怎么做。
我一直坚持这个问题,所以突破会很棒。
您不应使用树状单元格来检查所选值。您的 ChangeListener 已经直接接收到新值:
emtTree.getSelectionModel().selectedItemProperty().addListener(
(observable, oldSelection, newSelection) -> {
if (newSelection != null) {
TreeItemProperties<String, Integer> treeItem = newSelection;
generateEquipmentPanes(treeItem.getPropertyValue());
}
});
我正在构建一个应用程序,我需要在显示数据的窗格旁边有一个树视图。当有人在树视图中选择一个项目时,应用程序必须识别他们选择的内容并从数据库中寻找正确的数据,然后以我选择的格式呈现它。用户如何选择树视图中的项目(鼠标单击、Tab 键、箭头键等)一定无关紧要,只要当项目获得焦点时,就会触发一个方法将数据呈现给用户。
我通过以下方式使 仅鼠标点击 完美运行:
// Application thread method to build the tree map, used in the generateTree
// method.
public void treeBuilder(TreeMap<ModelSites, ArrayList<ModelPlants>> map) {
TreeMap<ModelSites, ArrayList<ModelPlants>> treeMap = map;
final TreeItemProperties<String, String> rootTreeItem = new TreeItemProperties<String, String>("EMT", null);
TreeItemProperties<String, Integer> site = null;
TreeItemProperties<String, Integer> plant = null;
for (Map.Entry<ModelSites, ArrayList<ModelPlants>> entry : treeMap.entrySet()) {
site = new TreeItemProperties<String, Integer>(entry.getKey().getLongName(), entry.getKey().getPrimaryKey());
rootTreeItem.getChildren().add(site);
if (site.getValue().equalsIgnoreCase("test item")) {
site.setExpanded(true);
}
for (int i = 0; i < entry.getValue().size(); i++) {
plant = new TreeItemProperties<String, Integer>(entry.getValue().get(i).getSitePlantId() + " " + entry.getValue().get(i).getShortName(), entry.getValue().get(i).getPrimaryKey());
site.getChildren().add(plant);
}
}
//Cell Factory is used to effectively turn the tree items into nodes, which they are not natively.
//This is necessary to have actions linked to the tree items (eg. double click an item to open an edit window).
emtTree.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {
@Override
public TreeCell<String> call(TreeView<String> param) {
FactoryTreeCell<String> cell = new FactoryTreeCell<String>();
cell.setOnMouseClicked(event -> {
if (!cell.isEmpty()) {
@SuppressWarnings("unchecked")
TreeItemProperties<String, Integer> treeItem = (TreeItemProperties<String, Integer>) cell.getTreeItem();
generateEquipmentPanes(treeItem.getPropertyValue());
}
});
return cell;
}
});
rootTreeItem.setExpanded(true);
emtTree.setRoot(rootTreeItem);
}
// Populate the main screen with all equipment items in the selected plant.
public void generateEquipmentPanes(int plantId) {
int plant = plantId;
Task<LinkedList<ModelEquipment>> task = new Task<LinkedList<ModelEquipment>>() {
@Override
public LinkedList<ModelEquipment> call() {
LinkedList<ModelEquipment> equipmentList = DAOEquipment.listEquipmentByPlant(plant);
return equipmentList;
}
};
// When list is built successfully, send the results back to the application
// thread to load the equipment panes in the GUI.
task.setOnSucceeded(e -> equipmentPaneBuilder(task.getValue()));
task.setOnFailed(e -> task.getException().printStackTrace());
task.setOnCancelled(null);
String methodName = new Object() {}.getClass().getEnclosingMethod().getName();
Thread thread = new Thread(task);
thread.setName(methodName);
//System.out.println("Thread ID: " + thread.getId() + ", Thread Name: " + thread.getName());
thread.setDaemon(true);
thread.start();
}
// Application thread method to build the equipment panes, used in the
// generateEquipmentPanes method.
public void equipmentPaneBuilder(LinkedList<ModelEquipment> list) {
LinkedList<ModelEquipment> equipmentList = list;
EquipmentPanels.getChildren().clear();
for (int i = 0; i < equipmentList.size(); i++) {
ModelEquipment item = equipmentList.get(i);
try {
PaneEquipment equipmentPane = new PaneEquipment();
equipmentPane.updateFields(item.getTechId(), item.getShortName(), item.getLongDesc()); equipmentPane.setId("equipPane" + i);
EquipmentPanels.getChildren().add(equipmentPane);
} catch (Exception e) {
e.printStackTrace();
}
}
}
我进行了大量的搜索,我有点想出了如何实现监听器而不是处理程序,因为这似乎是做我想做的事情的方法 - 将监听器放在 属性细胞。但是当用监听器替换事件处理程序时,就像下面的两个例子,我遇到了很多问题。
emtTree.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
@SuppressWarnings("unchecked")
TreeItemProperties<String, Integer> treeItem = (TreeItemProperties<String, Integer>) cell.getTreeItem();
generateEquipmentPanes(treeItem.getPropertyValue());
}
});
cell.focusedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (!cell.isEmpty()) {
@SuppressWarnings("unchecked")
TreeItemProperties<String, Integer> treeItem = (TreeItemProperties<String, Integer>) cell.getTreeItem();
generateEquipmentPanes(treeItem.getPropertyValue());
}
}
});
开始时,每次单击来自行 generateEquipmentPanes(treeItem.getPropertyValue());
的树项时,我都会得到 nullpointerexceptions
。其次,它往往会从错误的项目中选取数据,而不是我选择的项目。然后在点击几下之后它似乎完全崩溃并且除了提供更多 nullpointerexceptions
.
据我所知,我认为问题在于侦听器相对于需要传递给方法 generateEquipmentPanes
的变量的位置。还有一些关于在某个时间点删除侦听器并稍后重新添加它们的内容。
我应该以某种方式将监听器放入细胞工厂吗?目前它看起来像这样:
import javafx.scene.control.TreeCell;
public class FactoryTreeCell<T> extends TreeCell<T> {
public FactoryTreeCell() {
}
/*
* The update item method simply displays the cells in place of the tree items (which disappear when setCellFactory is set.
* This can be used for many more things (such as customising appearance) not implemented here.
*/
@Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null); //Note that a graphic can be many things, not just an image. Refer openJFX website for more details.
} else {
setText(item.toString());
}
}
}
我注意到的另一件事是还有其他方法可以实现 Callback
,这可能更适合听众,但我不知道该怎么做。
我一直坚持这个问题,所以突破会很棒。
您不应使用树状单元格来检查所选值。您的 ChangeListener 已经直接接收到新值:
emtTree.getSelectionModel().selectedItemProperty().addListener(
(observable, oldSelection, newSelection) -> {
if (newSelection != null) {
TreeItemProperties<String, Integer> treeItem = newSelection;
generateEquipmentPanes(treeItem.getPropertyValue());
}
});