0

この機能を実装する方法を知りたい:

ノードの名前を編集できる編集可能な JTree があります。ブランチ ノード (いくつかのリーフ ノードが含まれる) であるノードがあり、編集中にこのブランチ ノードが展開されると、編集後に、このノードは折りたたまれます。

編集が完了した後、ブランチノードが開いている場合は開いたままにし、折りたたまれている場合は折りたたんでおきたいです。

TreeWillExpandListenerを見ようとしましたが、これらのメソッドを呼び出す前に実際のノードが編集モードであるかどうかを認識する必要があるため、問題は解決しないようです...

このトリックを行う方法は?必要なことであることは明らかですが、答えがまったく見つかりません:/

これがコードです。説明します。まず、TreeModel を実装する ContactTreeModel クラスがあります。コンストラクターは、メイン アプリ フレームからアドレス帳とグループ マネージャーをロードするだけです。新しいルートを作成し、2 番目のメソッドでデータベースからデータをロードします。

public ContactTreeModel() {
    addressBookManager = ContactManagerFrame.getAddressBookManager();
    groupManager = ContactManagerFrame.getGroupManager();
    root = new DefaultMutableTreeNode();
    processTreeHierarchy();
}

private void processTreeHierarchy() {
    DefaultMutableTreeNode group, contact;
    for (Group g : addressBookManager.getGroups()) {
        group = new DefaultMutableTreeNode(g);
        root.add(group);
        for (Contact c : addressBookManager.getContactsFromGroup(g)) {
            contact = new DefaultMutableTreeNode(c);
            group.add(contact);
        }
    }
}

次の場合、TreeModelのメソッドvalueForPathChangedが起動されることを読みました。

ユーザーが newValue へのパスで識別される項目の値を変更したときにメッセージが送信されます。newValue が本当に新しい値を意味する場合、モデルは treeNodesChanged イベントをポストする必要があります。

だから私はその方法を次のように書いた:

@Override
public void valueForPathChanged(TreePath path, Object newValue) {
    // backup of the original group
    Group oldGroup = (Group) path.getLastPathComponent();
    try {
        Group testGroup = (Group) path.getLastPathComponent();
        testGroup.setName((String) newValue);
        // validation of the group to be updated
        groupManager.validateGroup(testGroup, true);
        oldGroup.setName((String) newValue);
        // updating of the group in db
        groupManager.updateGroup(oldGroup);
    } catch (ServiceFailureException | ValidationException ex) {
        // if database error occured or validation exception is raised, 
        // update label in gui 
        ContactManagerFrame.getStatusPanelLabel().setText(ex.getMessage());
    } finally {
        fireTreeStructureChanged();
    }
}

finally ブロックのメソッドに注意してください。このメソッドは常に起動されます。のように見えます

protected void fireTreeStructureChanged() {
    Object[] o = {root};
    TreeModelEvent e = new TreeModelEvent(this, o);
    for (TreeModelListener l : treeModelListeners) {
        l.treeNodesChanged(e);
    }
}

これは少しトリッキーであり、それが機能しない理由です (私は推測します)。すべての treeModelListeners を繰り返し処理し、treeNodesChanged メソッドを起動するだけです。

ContactTreeModel および関連するメソッドのプライベート属性として、ツリー モデル リスナーに指定された配列もあります。

private Vector<TreeModelListener> treeModelListeners = new Vector<>();

@Override
public void addTreeModelListener(TreeModelListener l) {
    treeModelListeners.addElement(l);
}

@Override
public void removeTreeModelListener(TreeModelListener l) {
    treeModelListeners.removeElement(l);
}

最後に、モデルに追加したモデル リスナーはどのように見えるでしょうか。ここに来ます:

public class ContactTreeModelListener implements TreeModelListener {
    @Override
    public void treeNodesChanged(TreeModelEvent e) {
        System.out.println("nodes changed");
    }

    @Override
        public void treeNodesInserted(TreeModelEvent e) {
        System.out.println("nodes inserted");
    }

    @Override
    public void treeNodesRemoved(TreeModelEvent e) {
        System.out.println("nodes removed");
    }

    @Override
    public void treeStructureChanged(TreeModelEvent e) {
        System.out.println("structure changed");
    }
}

したがって、基本的に何もしません。別の場所でリスナーを登録しましたが、今は関係ありません。

ということで、実行するとこの状態でツリーが崩れず、思い通りの動作になりました。ただし、そのノード (ラベル) の名前を実際に書き換えても、元の文字列が約 5 文字で、たとえば 4 文字に変更した場合、ラベルには 4 文字しかなく、5 番目の空白スペースもあります。そのため、そのラベルの編集を終了した後、元のラベルのサイズは変更されていません。同様に、グループの名前を拡張すると、たとえば名前を 4 から 5 文字に変更すると、そのノードのラベルに、テキスト全体が大きすぎて表示できないことを示すドットが含まれます。それは変です...どうすればそのラベルの更新を行うことができますか?

最後に... JTree にカスタム アイコンがあるので、空グループと空でないグループを認識します。ツリーで何らかのアクションを実行するたびに、db で実際のアイコンを確認する必要があります (確認しました)。ノードを開いて閉じるだけでも実行されます)。これは非常に非効率なので、実際のキャッシュを行う DefaultTreeCellRenderer を拡張しました。

@Override
public Component getTreeCellRendererComponent(
        JTree tree,
        Object value,
        boolean sel,
        boolean expanded,
        boolean leaf,
        int row,
        boolean hasFocus) {

    if (iconCache == null) {
        throw new NullPointerException("iconCache in " 
             + this.getClass().getName() + " is null");
    }

    super.getTreeCellRendererComponent(
            tree, value, sel,
            expanded, leaf, row,
            hasFocus);

    if (value instanceof Group) {
        Group g = (Group) value;
        Icon groupIcon = iconCache.get(g);
        if (groupIcon == null) {
            if (groupHasContacts(g)) {
                groupIcon = groupNonEmpty;
            } else {
                groupIcon = groupEmptyIcon;
            }
            iconCache.put(g, groupIcon);
        }
        JLabel result = (JLabel) super.getTreeCellRendererComponent(tree,
                g.getName(), sel, expanded, leaf, row, hasFocus);

        result.setIcon(groupIcon);
        return result;
    }
    else if (value instanceof Contact) {
        Contact c = (Contact) value;
        Icon icon = iconCache.get(c);
        if (icon == null) {
            icon = this.contactIcon;
            iconCache.put(c, icon);
        }
        JLabel result = (JLabel) super.getTreeCellRendererComponent(
                tree, c.getName() + c.getSurname(), 
                sel, expanded, leaf, row, hasFocus);
        result.setIcon(icon);
        return result;
    }

    JLabel defaultNode = (JLabel) super.getTreeCellRendererComponent(
            tree, "?", sel, expanded, leaf, row, hasFocus);

    defaultNode.setIcon(unknownNode);
    return defaultNode;
}
4

1 に答える 1

0

適切に再描画されないラベルの更新に関する問題は、パスを識別する配列treeNodesChangedでのみイベントを発生させるという事実に関連している可能性があります。root

から次の呼び出しを試みますvalueForPathChanged

public void fireTreeNodesChanged(TreePath path) {
    TreeModelEvent e = new TreeModelEvent(this, path.getPath());
    for (TreeModelListener l : treeModelListeners) {
        l.treeNodesChanged(e);
    }
}

ちなみに、fireTreeStructureChanged実際に発火するというあなたの名前treeNodesChangedは非常に誤解を招くものです。

于 2012-05-03T23:16:54.573 に答える