我是关于GUI编程的新手,也许是我的问题所在 一个非常简单的解决方 我试图实现一个Java Swing GUI作为一个 编辑数据结构的树。 GUI分为三个 部分:
窗口左四分之一的树查看器显示 树结构化数据。
右上方的大区域显示包含文本字段的编辑器, 桌子等。树中的每种不同类型的对象 结构有自己的编辑器,在选中时显示 树查看器。
右下方区域显示控制台查看器。它用于显示 有关具体行动的消息。
我努力遵守模式与模式的严格分离 我的程序中的树查看器/编辑器中的可视化。所以我 创建了一个DefaultTreeModel(MyTreeModel)子类的实例 它存储对业务数据的引用和a的实例 JTree的子类,提供树的可视化表示 结构
我试图实现使用修改数据的功能 行动班。任何动作(如CreateNode,RenameNode,DeleteNode) 在它自己的动作类(AbstractAction的子类)中实现。 这些操作在树查看器的上下文菜单中使用 应用程序"编辑"菜单。但我也希望重用其中的一些 GUI的编辑器部分,例如RenameNode操作。我在这里 目前卡住了。
树查看器显示一个图标以及其中每个节点的名称 那个树。并且相应的编辑器包含其中之一 stuff,也是一个显示相关节点名称的JTextField。
我知道我可以将动作对象附加到JMenu,JPopupMenu和 甚至使用setAction方法到JTextField对象。一世 这样做了,现在我有一个"重命名"在节目中加入菜单"编辑" menue,在与代表的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)方法。节点的真正变化 name在方法中的树的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类(它是alrady的一个子类) DefaultMutableTreeNode)必须实现接口Document 并且RenameAction类必须修改它然后修改一个事件 必须发出通知显示的树查看器 改变了节点。
结论:我必须承认我还没有完全理解 正确实现要在多个地方使用的操作 在GUI中,我不完全了解如何实现模型 可以被多个GUI对象使用(在我的例子中是一个JTree和 一个JTextField)。可能一切都很简单......
提前感谢您的帮助!
所给出的答案对解释行动方式非常有帮助 可以与JTrees一起使用。但还有一点我想讨论。 在我的GUI中,我有一个结合了我的业务数据的树形图 使用数据的编辑器(树位于窗口的左四分之一,除此之外是节点类型特定的编辑器)。所有节点都有可以更改的名称。并且,编辑器包含一个文本字段(用JTextField实现),其中显示节点的名称,也可以编辑该名称。我的不确定性如下: JTextField允许分配动作对象以及动作对象 模特吧。实际上,该模型将是已在JTree中查看的节点对象。我认为应该有一种方法可以使用JTree中使用的相同模型对象作为编辑器中JTextField的模型,并在那里重用Action类。关于模型的重用,我认为我的模型类MyTreeNode也必须实现Document接口,对吗?在启动特定于节点的编辑器时,我必须使用其setDocument()方法将当前在JTree中选择的节点与JTextField对象相关联。 最后我的RenameNodeAction必须执行更改 JTextField的节点名称。 所以我的中心观点是:使一个模型在多个视图中显示,并在重定位节点的任何地方重用仅一个RenameAction。这是否有意义,我的想法是如何实现这一点的可行性?
答案 0 :(得分:3)
超出Stackoverflow范围的应用程序设计的完整指南。相反,请从How to Use Trees中显示的示例TreeIconDemo
开始。请注意它如何向TreeSelectionListener
添加tree
以更新附近的JEditorPane
。现在,将另一个TreeSelectionListener
添加到其他视图,以了解如何更新新视图。您也可以从这个相关的answer获得一些见解。
附录:从此example开始,您可以执行以下操作。更改选择会更新textField
以显示所选节点的名称。编辑节点(通常为 F2 )或textField
会更改所选节点的名称。
private JTextField textField = new JTextField(10);
...
final DefaultTreeModel treeModel = new DefaultTreeModel(root);
tree = new JTree(treeModel);
tree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
TreePath path = e.getNewLeadSelectionPath();
if (path != null) {
DefaultMutableTreeNode node =
(DefaultMutableTreeNode) path.getLastPathComponent();
if (node.isLeaf()) {
Resource user = (Resource) node.getUserObject();
textField.setText(user.toString());
} else {
textField.setText("");
}
}
}
});
textField.addActionListener(new AbstractAction("edit") {
@Override
public void actionPerformed(ActionEvent e) {
TreePath path = tree.getSelectionPath();
if (path != null) {
DefaultMutableTreeNode node =
(DefaultMutableTreeNode) path.getLastPathComponent();
if (node.isLeaf()) {
String s = textField.getText();
Resource user = (Resource) node.getUserObject();
user.setName(s);
treeModel.reload(node);
}
}
}
});
答案 1 :(得分:1)
好的,我用JTable
做了类似的事情(插入/删除行)。
基本上你想要一个可以引用Action
的{{1}}。然后可以将此JTree
提供给您的文件菜单,弹出菜单和分配给击键的事件。如果您有效地编写代码,则可以共享相同的操作,但这不是必需的。
你想要做的是让视图和模型做他们想做的事,你的行动只是启动过程的催化剂。
所以基本上你可以这样做:
Action
为了使其更高级,您可以将public class RenameNodeAction extends AbstractAction {
private JTree tree;
public RenameNodeAction(JTree tree) {
this.tree = tree;
// Initialise action as you require...
}
// Access to the tree, provide mostly so you can extend the action
public JTree getTree() {
return tree;
}
public void actionPerformed(ActionEvent evt) {
JTree tree = getTree();
TreePath path = tree.getSelectionPath();
if (path != null && tree.isPathEditable(path)) {
tree.startEditingAtPath(path);
}
}
}
附加到提供的树,并根据选择更改操作的TreeSelectionListener
状态。
因此,当没有选择任何内容时,您将禁用该操作,如果所选内容不可编辑,您将禁用该选择等。
这意味着(正如您正确地尝试实现的)是实现此目的的代码是集中的和可重用的。您可以将相同的操作(相同的实例或多个实例)应用于“文件”菜单,工具栏按钮,enabled
,弹出菜单和击键,并确保为每个操作执行相同的代码。
答案 2 :(得分:1)
不完全确定,但我认为你正在寻找类似的东西
public class RenameNode extends AbstractAction
implements TreeSelectionListener, CellEditorListener {
// Replace with whatever you're storing your text fields with
private FieldStorage fields;
private JTree tree;
public RenameNode(FieldStorage fields, JTree tree) {
this.fields = fields;
this.tree = tree;
}
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());
}
}
public void valueChanged(TreeSelectionEvent e) {
tree.startEditingAtPath(tree.getSelectionPath());
setFieldText();
}
public void editingCanceled(ChangeEvent e) {
// empty
}
public void editingStopped(ChangeEvent e) {
setFieldText();
}
private void setFieldText() {
MyTreeNode node = (MyTreeNode) tree.getSelectionPath()
.getLastPathComponent();
String text = node.getUserObject().toString();
fields.getFieldFor(node).setText(text);
}
}
然后,当您初始化树时,添加
RenameNode renameNodeAction = new RenameNode(fields, tree);
tree.addTreeSelectionListener(renameNodeAction);
// editor is your TreeCellEditor. Can be the DefaultTreeCellEditor
// if you haven't already made your own
editor.addCellEditorListener(renameNodeAction);
tree.setCellEditor(editor);
tree.setEditable(true);
这会将您的树编辑与您的字段同步。
顺便说一下,我觉得你很好地坚持MVC。而且,你的设计听起来很复杂。您可能希望将某些功能重新考虑为更精简的功能。