5

ツリーの状態 (展開/選択されたノード) の保存に取り組んでおり、ノードの状態を保存および復元できるユーティリティ クラスを作成しました。それは正常に動作します。

ただし、JTree 自体にはまだ 1 つの問題があります。ユーザーが JTree インスタンス (ノードの展開/折りたたみ) で作業しているときに、(折りたたまれた別のノードの下に隠れている) 一部のノードが展開される場合があります。それについて特別なことは何もありません-それは問題ありません。

JTree は、expandedStateノード パスをキーとして使用し、ブール値を展開状態値として使用して、展開/折りたたまれたノードに関する記録を別の Hashtable に保持します。そのため、折りたたまれた親ノードの下にある展開されたノードが表示される場合、expandedStateHashtable にtrue値を持つレコードがあるため、展開されたままになります。

スクリーンショットで説明されている状況...
1. ルートを展開し、ルートの下のいくつかのノード (「glassfish4
ここに画像の説明を入力

ここに画像の説明を入力
フォルダー) を展開します。
ここに画像の説明を入力

ルートが折りたたまれたスクリーンショット #2 の瞬間にツリーの状態を保存したと想像してください。問題は、すべてのツリー ノードの状態を復元したい場合 (非表示のノードであっても)、別の折りたたまれたノードの下にあるノードを展開できないことです。すべての親ノードを強制的に展開します。またexpandedState、Hashtable は JTree でプライベートに宣言されており、アクセスする良い方法がないため、Hashtable に直接アクセスして展開された状態を変更することはできません。したがって、初期のツリー状態を完全に再現することはできません。

だから私ができることは次のとおりです。

  1. リフレクションを介してその Hashtable に強制的にアクセスする - 本当に悪い考え
  2. JTree ノードの展開ロジックを書き直す - これも悪い考えです
  3. 最初にすべての展開された状態を復元してから、すべての折りたたまれた状態を復元します。これは、ツリーに無意味な再描画と多くの追加のレンダリングを強制するため、使用したくない非常に悪い回避策です。

多分私は何か他のものを見逃していますか?

したがって、基本的な質問は次のとおり
です。親ノードを展開せずに子ノードを展開する他の方法はありますか?

ツリーの状態を保存/復元するために使用するいくつかのクラスを以下に示します。

TreeUtils.getTreeState(tree)JTree の状態を取得し、JTree の状態TreeUtils.setTreeState(tree,treeState)を復元するために呼び出すだけです。ツリーは UniqueNode を使用する必要があることに注意してください。そうしないと、これらのメソッドは ClassCastException をスローします。DefaultMutableTreeNode を拡張する独自のノードがある場合は、単純に DefaultMutableTreeNode を UniqueNode に置き換えることができます。

UniqueNode.java - 独自の一意の ID を持つ単純なノード

public class UniqueNode extends DefaultMutableTreeNode implements Serializable
{
    /**
     * Prefix for node ID.
     */
    private static final String ID_PREFIX = "UN";

    /**
     * Unique node ID.
     */
    protected String id;

    /**
     * Costructs a simple node.
     */
    public UniqueNode ()
    {
        super ();
        setId ();
    }

    /**
     * Costructs a node with a specified user object.
     *
     * @param userObject custom user object
     */
    public UniqueNode ( Object userObject )
    {
        super ( userObject );
        setId ();
    }

    /**
     * Returns node ID and creates it if it doesn't exist.
     *
     * @return node ID
     */
    public String getId ()
    {
        if ( id == null )
        {
            setId ();
        }
        return id;
    }

    /**
     * Changes node ID.
     *
     * @param id new node ID
     */
    public void setId ( String id )
    {
        this.id = id;
    }

    /**
     * Changes node ID to new random ID.
     */
    private void setId ()
    {
        this.id = TextUtils.generateId ( ID_PREFIX );
    }

    /**
     * {@inheritDoc}
     */
    public UniqueNode getParent ()
    {
        return ( UniqueNode ) super.getParent ();
    }

    /**
     * Returns TreePath for this node.
     *
     * @return TreePath for this node
     */
    public TreePath getTreePath ()
    {
        return new TreePath ( getPath () );
    }
}

TreeUtils.java - JTree との間で TreeState を保存/ロードするユーティリティ クラス

public class TreeUtils
{
    /**
     * Returns tree expansion and selection states.
     * Tree nodes must be instances of UniqueNode class.
     *
     * @param tree tree to process
     * @return tree expansion and selection states
     */
    public static TreeState getTreeState ( JTree tree )
    {
        return getTreeState ( tree, true );
    }

    /**
     * Returns tree expansion and selection states.
     * Tree nodes must be instances of UniqueNode class.
     *
     * @param tree          tree to process
     * @param saveSelection whether to save selection states or not
     * @return tree expansion and selection states
     */
    public static TreeState getTreeState ( JTree tree, boolean saveSelection )
    {
        TreeState treeState = new TreeState ();

        List<UniqueNode> elements = new ArrayList<UniqueNode> ();
        elements.add ( ( UniqueNode ) tree.getModel ().getRoot () );
        while ( elements.size () > 0 )
        {
            UniqueNode element = elements.get ( 0 );

            TreePath path = new TreePath ( element.getPath () );
            treeState.addState ( element.getId (), tree.isExpanded ( path ), saveSelection && tree.isPathSelected ( path ) );

            for ( int i = 0; i < element.getChildCount (); i++ )
            {
                elements.add ( ( UniqueNode ) element.getChildAt ( i ) );
            }

            elements.remove ( element );
        }

        return treeState;
    }

    /**
     * Restores tree expansion and selection states.
     * Tree nodes must be instances of UniqueNode class.
     *
     * @param tree      tree to process
     * @param treeState tree expansion and selection states
     */
    public static void setTreeState ( JTree tree, TreeState treeState )
    {
        setTreeState ( tree, treeState, true );
    }

    /**
     * Restores tree expansion and selection states.
     * Tree nodes must be instances of UniqueNode class.
     *
     * @param tree             tree to process
     * @param treeState        tree expansion and selection states
     * @param restoreSelection whether to restore selection states or not
     */
    public static void setTreeState ( JTree tree, TreeState treeState, boolean restoreSelection )
    {
        if ( treeState == null )
        {
            return;
        }

        tree.clearSelection ();

        List<UniqueNode> elements = new ArrayList<UniqueNode> ();
        elements.add ( ( UniqueNode ) tree.getModel ().getRoot () );
        while ( elements.size () > 0 )
        {
            UniqueNode element = elements.get ( 0 );
            TreePath path = new TreePath ( element.getPath () );

            // Restoring expansion states
            if ( treeState.isExpanded ( element.getId () ) )
            {
                tree.expandPath ( path );

                // We are going futher only into expanded nodes, otherwise this will expand even collapsed ones
                for ( int i = 0; i < element.getChildCount (); i++ )
                {
                    elements.add ( ( UniqueNode ) tree.getModel ().getChild ( element, i ) );
                }
            }
            else
            {
                tree.collapsePath ( path );
            }

            // Restoring selection states
            if ( restoreSelection )
            {
                if ( treeState.isSelected ( element.getId () ) )
                {
                    tree.addSelectionPath ( path );
                }
                else
                {
                    tree.removeSelectionPath ( path );
                }
            }

            elements.remove ( element );
        }
    }
}

TreeState.java - ノードの状態を保持するマップのコンテナー クラス

public class TreeState implements Serializable
{
    /**
     * Tree node states.
     */
    protected Map<String, NodeState> states = new LinkedHashMap<String, NodeState> ();

    /**
     * Constructs new object instance with empty states.
     */
    public TreeState ()
    {
        super ();
    }

    /**
     * Constructs new object instance with specified states.
     *
     * @param states node states
     */
    public TreeState ( Map<String, NodeState> states )
    {
        super ();
        if ( states != null )
        {
            setStates ( states );
        }
    }

    /**
     * Returns all node states.
     *
     * @return all node states
     */
    public Map<String, NodeState> getStates ()
    {
        return states;
    }

    /**
     * Sets all node states.
     *
     * @param states all node states
     */
    public void setStates ( Map<String, NodeState> states )
    {
        this.states = states;
    }

    /**
     * Adds node state.
     *
     * @param nodeId   node ID
     * @param expanded expansion state
     * @param selected selection state
     */
    public void addState ( String nodeId, boolean expanded, boolean selected )
    {
        states.put ( nodeId, new NodeState ( expanded, selected ) );
    }

    /**
     * Returns whether node with the specified ID is expanded or not.
     *
     * @param nodeId node ID
     * @return true if node with the specified ID is expanded, false otherwise
     */
    public boolean isExpanded ( String nodeId )
    {
        final NodeState state = states.get ( nodeId );
        return state != null && state.isExpanded ();
    }

    /**
     * Returns whether node with the specified ID is selected or not.
     *
     * @param nodeId node ID
     * @return true if node with the specified ID is expanded, false otherwise
     */
    public boolean isSelected ( String nodeId )
    {
        final NodeState state = states.get ( nodeId );
        return state != null && state.isSelected ();
    }
}

NodeState.java - 単一ノードの展開/選択状態

public class NodeState implements Serializable
{
    /**
     * Whether node is expanded or not.
     */
    protected boolean expanded;

    /**
     * Whether node is selected or not.
     */
    protected boolean selected;

    /**
     * Constructs empty node state.
     */
    public NodeState ()
    {
        super ();
        this.expanded = false;
        this.selected = false;
    }

    /**
     * Constructs node state with the specified expansion and selection states.
     *
     * @param expanded expansion state
     * @param selected selection state
     */
    public NodeState ( boolean expanded, boolean selected )
    {
        super ();
        this.expanded = expanded;
        this.selected = selected;
    }

    /**
     * Returns whether node is expanded or not.
     *
     * @return true if node is expanded, false otherwise
     */
    public boolean isExpanded ()
    {
        return expanded;
    }

    /**
     * Sets whether node is expanded or not.
     *
     * @param expanded whether node is expanded or not
     */
    public void setExpanded ( boolean expanded )
    {
        this.expanded = expanded;
    }

    /**
     * Returns whether node is selected or not.
     *
     * @return true if node is selected, false otherwise
     */
    public boolean isSelected ()
    {
        return selected;
    }

    /**
     * Sets whether node is selected or not.
     *
     * @param selected whether node is selected or not
     */
    public void setSelected ( boolean selected )
    {
        this.selected = selected;
    }
}

ところで、setTreeStateメソッドは、現時点では、折りたたまれたノードの下で展開された状態を復元することを回避します。

        // Restoring expansion states
        if ( treeState.isExpanded ( element.getId () ) )
        {
            tree.expandPath ( path );

            // We are going futher only into expanded nodes, otherwise this will expand even collapsed ones
            for ( int i = 0; i < element.getChildCount (); i++ )
            {
                elements.add ( ( UniqueNode ) tree.getModel ().getChild ( element, i ) );
            }
        }
        else
        {
            tree.collapsePath ( path );
        }

親ノードが展開されている場合にのみ呼び出される子ノードを収集するメソッド。したがって、折りたたまれたノードの下にあるすべての子ノードは無視されます。その動作を変更すると、この質問の冒頭で説明した問題が表示されます-親ノードが展開されます。

4

1 に答える 1

1

説明と同じアクションを実行して状態を復元しないのはなぜですか。最初にサブ ノードを展開に設定し、次に必要に応じて親ノードを折りたたみに設定します。

現在のコードとの唯一の違いは、反復を 1 回ではなく 2 回使用することです。最初に必要な場所で繰り返して展開し、次に必要な場所で繰り返して折りたたむ。

いずれにせよ、再描画ロジックにより、ツリーは 1 回描画する必要があります。

于 2013-08-26T13:23:51.443 に答える