我是一个关于 GUI 编程的新手,也许我的问题有一个非常简单的解决方案。我正在尝试实现一个 Java Swing GUI,它充当树状数据结构的编辑器。GUI分为三个部分:
窗口左侧四分之一的树查看器显示树结构数据。
右上角的大区域显示包含文本字段、表格等的编辑器。树结构中的每种不同类型的对象都有自己的编辑器,当在树查看器中选择它时会显示该编辑器。
右下方区域显示了一个控制台查看器。它用于显示有关特定操作的消息。
我正在努力遵守模型与程序中树查看器/编辑器中的可视化的严格分离。因此,我创建了一个 DefaultTreeModel (MyTreeModel) 子类的实例,它存储对业务数据的引用,以及一个 JTree 子类的实例,它提供了树结构的可视化表示。
我正在尝试实现使用 Action 类修改数据的功能。任何动作(如 CreateNode、RenameNode、DeleteNode)都在它自己的动作类(AbstractAction 的子类)中实现。这些操作在树查看器的上下文菜单和应用程序“编辑”菜单中使用。但我也想在 GUI 的编辑器部分重用其中的一些,例如 RenameNode 操作。在这里,我目前被困住了。
树查看器显示一个图标以及树中每个节点的名称。除其他内容外,相应的编辑器还包含一个显示关联节点名称的 JTextField。
我知道我可以使用 setAction 方法将操作对象附加到 JMenu、JPopupMenu 甚至 JTextField 对象。我这样做了,现在我在程序的“编辑”菜单中有一个“重命名”菜单条目,在与代表树查看器的 JTree 关联的弹出菜单中,显示节点名称的 JTextField 也附加了此操作。
为了更改树节点的“名称”属性,我创建了类 RenameNode 作为 AbstractAction 的子类。如前所述,名称显示在树查看器中的每个节点上,并且该操作只是使该文本可编辑。执行此操作的代码如下所示(在 RenameNode 类中):
public void actionPerformed(ActionEvent ev) {
// "mouseOverPath" is the Treepath were the mouse was placed on
// when the popup menu was opened
if (tree.existsMouseOverPath()) {
tree.startEditingAtPath(tree.mouseOverPath);
} else if (tree.getSelectionCount() != 0) {
tree.startEditingAtPath(tree.getSelectionPath());
}
}
需要这些 if 语句才能使操作正常工作:
-- 弹出菜单(第一个 if 语句;这里打开弹出菜单时鼠标下的对象可编辑)
-- 应用程序的菜单(第二个 if 语句;这里当前选择的树节点 -- 如果有的话 -- 可编辑)。
嗯,这工作正常,原则上但实际上节点的重命名不是通过 RenameAction 类的 actionPerformed(ActionEvent ev) 方法中的代码完成的。节点名称的真正更改是在树的 MyTreeModel 类中的 valueForPathChanged() 方法中执行的,该方法被覆盖如下:
public class MyTreeModel extends DefaultTreeModel {
[...]
@Override
public void valueForPathChanged(TreePath path, Object newValue) {
final MyTreeNode aNode = (MyTreeNode)path.getLastPathComponent();
if (newValue instanceof String) {
((MyNode) aNode.getUserObject()).setName((String) newValue);
} else {
aNode.setUserObject(newValue);
}
nodeChanged(aNode);
}
[...]
}
我完全不知道如何在这里正确应用动作的概念。更糟糕的是执行重命名操作更改 JTextField 对象中的文本时的情况。目前我不知道如何以干净的方式实现它。JTextField 应与树模型结构中的单个节点作为其模型关联,并且附加的操作应修改该模型,并且当此模型更改时,树查看器需要得到通知以更新树查看器中相应节点的名称。
我假设 MyNode 类(它一直是 DefaultMutableTreeNode 的子类)必须实现接口 Document 并且 RenameAction 类必须修改它,然后必须发出一个事件来通知显示改变节点。
底线:我必须承认我还没有完全理解如何正确实现要在 GUI 中的多个位置使用的操作,并且我不完全理解如何实现可以被多个 GUI 对象使用的模型(在我的例子中是一个 JTree 和一个 JTextField)。可能这一切都很简单......
提前感谢您的帮助!
那么给出的答案对于解释如何将动作与 JTrees 一起使用非常有帮助。但还有一点我想讨论。在我的 GUI 中,我有一个业务数据的树表示与数据的编辑器相结合(树位于窗口的左四分之一处,旁边是一个节点类型特定的编辑器)。所有节点都有可以更改的名称。并且,编辑器包含一个文本字段(使用 JTextField 实现),其中显示了节点的名称并且也可以进行编辑。我的不确定性如下: JTextField 允许为它分配一个动作对象和一个模型。实际上,该模型将是已在 JTree 中查看的节点对象。我认为应该有一种方法可以使用 JTree 中使用的相同模型对象,也可以作为编辑器中 JTextField 的模型,并在那里重用 Action 类。关于模型的重用,我认为我的模型类 MyTreeNode 也必须实现 Document 接口,对吗?当启动节点特定的编辑器时,我必须使用其 setDocument() 方法将 JTree 中当前选择的节点与 JTextField 对象相关联。最后,我的 RenameNodeAction 必须更改 JTextField 的节点名称。所以我的中心点是:让一个模型显示在多个视图中,并在要重命名节点的任何地方重用一个 RenameAction。这有意义吗?我的想法是如何实现的?关于模型的重用,我认为我的模型类 MyTreeNode 也必须实现 Document 接口,对吗?当启动节点特定的编辑器时,我必须使用其 setDocument() 方法将 JTree 中当前选择的节点与 JTextField 对象相关联。最后,我的 RenameNodeAction 必须更改 JTextField 的节点名称。所以我的中心点是:让一个模型显示在多个视图中,并在要重命名节点的任何地方重用一个 RenameAction。这有意义吗?我的想法是如何实现的?关于模型的重用,我认为我的模型类 MyTreeNode 也必须实现 Document 接口,对吗?当启动节点特定的编辑器时,我必须使用其 setDocument() 方法将 JTree 中当前选择的节点与 JTextField 对象相关联。最后,我的 RenameNodeAction 必须更改 JTextField 的节点名称。所以我的中心点是:让一个模型显示在多个视图中,并在要重命名节点的任何地方重用一个 RenameAction。这有意义吗?我的想法是如何实现的?d 必须使用其 setDocument() 方法将 JTree 中当前选择的节点与 JTextField 对象相关联。最后,我的 RenameNodeAction 必须更改 JTextField 的节点名称。所以我的中心点是:让一个模型显示在多个视图中,并在要重命名节点的任何地方重用一个 RenameAction。这有意义吗?我的想法是如何实现的?d 必须使用其 setDocument() 方法将 JTree 中当前选择的节点与 JTextField 对象相关联。最后,我的 RenameNodeAction 必须更改 JTextField 的节点名称。所以我的中心点是:让一个模型显示在多个视图中,并在要重命名节点的任何地方重用一个 RenameAction。这有意义吗?我的想法是如何实现的?