Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
90 views
in Technique[技术] by (71.8m points)

java - Uncertainties regarding Implementation of Actions and Usage of a single Model with multiple Views

I'm a total newbee regarding GUI programming and maybe my problem has a quite simple solution. I'm trying to implement a Java Swing GUI that serves as an editor for a tree like data structure. The GUI is divided into three parts:

  1. A tree viewer in the left quarter of the window displays the tree structured data.

  2. The large upper right area displays editors containing text fields, tables and the like. Each different kind of object in the tree structure has its own editor which is shown when it is selected in the tree viewer.

  3. The lower right area shows a console viewer. It is used to display messages about specific actions.

I'm trying hard to obey a strict separation of the model from its visualization in the tree viewers/editors in my program. Therefore I created an instance of a sub-class of DefaultTreeModel (MyTreeModel) which stores references to the business data and an instance of a sub-class of JTree that provides the visual representation of the tree structure.

I'm attempting to implement functionality that modifies the data using Action classes. Any action (like CreateNode, RenameNode, DeleteNode) is implemented in it's own action class (sub-class of AbstractAction). The actions are used in the tree viewer's context menu and in the applications "Edit" menu. But I also want to reuse some of them in the editor parts of the GUI, e.g. the RenameNode action. And here I'm currently stuck.

The tree viewer displays an icon along with the name for every node in the tree. And the respective editor contains, among other stuff, also a JTextField that shows the associated node's name.

I know that I can attach action objects to JMenu, JPopupMenu and even to JTextField objects using the setAction method. I did so and now I have a "Rename" menue entry in the program's "Edit" menue, in the popup menue associated with the JTree that represents the tree viewer and the JTextField showing the node name also has this action attached to it.

To change the "Name" attribute of a tree node, I created the class RenameNode as a sub-class of AbstractAction. As already mentioned the name is displayed at each node in the tree viewer and the action simply makes this text editable. The code doing this looks as follows (in the class 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());
    }
}

These if statements are needed to make the action work properly from:

-- the popup menu (first if statement; here the object which was under the mouse when the popup menu is opened is made editable)

-- the application's menu (second if statement; here the tree node that is currently selected -- if any -- is made editable).

Well, this works fine, in principle but in fact the renaming of the node is not done through the code in the RenameAction class's actionPerformed(ActionEvent ev) method. The real change of the node's name is executed in the tree's MyTreeModel class in the method valueForPathChanged() which is overridden as follows:

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);
    }

[...]

}

I have absolutely no clue how the concept of actions could properly be applied here. Even worse is the situation with the rename operation when it is performed changing the text in the JTextField object. At the moment I don't know how to implement that in a clean way. The JTextField should get associated with a single node from the tree model structure as its model and the attached action should modify that model and when this model is changed the tree viewer would need to get notified to update the respective node's name in the tree viewer.

I assume that the MyNode class (which is alrady a sub-class of DefaultMutableTreeNode) would have to implement the interface Document and the RenameAction class would have to modify it and then an event would have to be issued to notify the tree viewer that displays the changed node.

Bottom line: I must admit that I did not yet completely understand how to properly implement an action that is to be used in multiple places in a GUI and I don't completely understand how to implement a model that can be used by multiple GUI objects (in my case a JTree and a JTextField). Possibly all that is quite simple ...

Thanks in advance for any help!

Well the answers given were quite helpful in explaining how actions can be used together with JTrees. But there is one more point I'd like to discuss. In my GUI I have a tree representation of my business data combined with editors for the data (the tree located in the left quarter of the window and aside of it a node type specific editor). All the nodes have names that can be changed. And, the editors contain a text field (implemented with a JTextField) in which the node's name is displayed and which can be edited too. My uncertainty here is as follows: A JTextField allows to assign an action object as well as a model to it. Actually the model would be a node object already viewed in the JTree. I think there should be a way to use the same model object used in the JTree also as a model for the JTextField in the editor and also reuse the Action class there. Regarding the reuse of the model I think my model class MyTreeNode will have to implement the Document interface too, correct? And when bringing up the node specific editor I'd have to associate the node currently selected in the JTree with the JTextField object using its setDocument() method. Finally my RenameNodeAction would have to perform the change of the JTextField's node's name. So my central point is: making one model being displayd in multiple views and the reuse of just one RenameAction everywhere where a node is to be renamed. Does this make sense and is my idea how this must be accomplished feasible?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

A complete guide to application design in beyond the scope of Stackoverflow. Instead, start with the example TreeIconDemo shown in How to Use Trees. Notice how it adds a TreeSelectionListener to the tree in order to update a nearby JEditorPane. Now, add another TreeSelectionListener to a different view to see how you could update the new view, too. You might also get some insight from this related answer.

Addendum: Starting from this example, you can do something like the following. Changing the selection updates the textField to show the selected node's name. Editing either the node (typically F2) or the textField changes the selected node's name.

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);
            }
        }
    }
});

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...