关于具有多个视图的单个模型的操作和使用的实现的不确定性

时间:2012-07-24 17:12:21

标签: java swing model-view-controller

我是关于GUI编程的新手,也许是我的问题所在 一个非常简单的解决方 我试图实现一个Java Swing GUI作为一个 编辑数据结构的树。 GUI分为三个 部分:

  1. 窗口左四分之一的树查看器显示 树结构化数据。

  2. 右上方的大区域显示包含文本字段的编辑器, 桌子等。树中的每种不同类型的对象 结构有自己的编辑器,在选中时显示 树查看器。

  3. 右下方区域显示控制台查看器。它用于显示 有关具体行动的消息。

  4. 我努力遵守模式与模式的严格分离 我的程序中的树查看器/编辑器中的可视化。所以我 创建了一个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。这是否有意义,我的想法是如何实现这一点的可行性?

3 个答案:

答案 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。而且,你的设计听起来很复杂。您可能希望将某些功能重新考虑为更精简的功能。