3

を実装するカスタムJTree実装がありconvertValueToTextます。この実装は、グローバルな状態に依存します。返された文字列が以前に返された文字列よりも長い場合(実際にはピクセルのように幅が広いと思います)、テキストは切り捨てられ、「...」が埋め込まれます。repaintこれは、ツリー上の要素またはaを(非)選択することによって再描画が発生した場合に当てはまります。

そのような方法で実装することは有効ですconvertValueToTextか?(ツリーの表示は自動的に更新されないことを認識しています)。

「...」を取り除き、すべての要素を現在のテキスト値で正しく描画するにはどうすればよいですか?

Update2:

どうやら私はSwingsのかなり厳格なトレッドポリシーに違反したようです(http://docs.oracle.com/javase/6/docs/api/javax/swing/package-summary.html)。更新の実行:

        SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                for (int row = 0; row < tree.getRowCount(); row++) {
                    ((DefaultTreeModel) tree.getModel())
                            .nodeChanged((TreeNode) tree.getPathForRow(row)
                                    .getLastPathComponent());
                }
            }
        });

正しく機能しているようです。

アップデート:

最小限の例を次に示します。

import java.util.Enumeration;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;

public class WeirdTreeFrame extends JFrame {
    public class WeirdTree extends JTree {
        public WeirdTree(TreeNode root) {
            super(root);
        }

        @Override
        public String convertValueToText(Object value, boolean selected,
                boolean expanded, boolean leaf, int row, boolean hasFocus) {
            return super.convertValueToText(value, selected, expanded, leaf, row, hasFocus);
        }
    }

    public class WeirdNode implements TreeNode {
        protected WeirdNode parent;
        protected WeirdNode children[];
        protected int dephth;
        protected String name;
        protected String names[] = {"Foo", "Bar", "Ohh"};
        public long superCrazyNumber;

        public WeirdNode(WeirdNode parent, String name) {
            this.parent = parent; 
            this.name = name;
            if (parent != null) {
                dephth = parent.dephth + 1;
            } else {
                dephth = 0;
            }
            if (dephth < 10) {
                children = new WeirdNode[3];
                for (int i = 0; i < 3; i++) {
                    children[i] = new WeirdNode(this, name + names[i]);
                }
            }
        }

        @Override
        public TreeNode getChildAt(int childIndex) {
            if (childIndex >= getChildCount()) return null;
            return children[childIndex];
        }

        @Override
        public int getChildCount() {
            return (children != null) ? children.length : 0;
        }

        @Override
        public TreeNode getParent() {
            return parent;
        }

        @Override
        public int getIndex(TreeNode node) {
            for (int i = 0; i < children.length; i++) {
                if (children[i] == node) return i;
            }
            throw new RuntimeException();
        }

        @Override
        public boolean getAllowsChildren() {
            return true;
        }

        @Override
        public boolean isLeaf() {
            return getChildCount() == 0;
        }

        @Override
        public Enumeration children() {
            return new Enumeration<TreeNode>() {
                int nextIdx = 0;
                @Override
                public boolean hasMoreElements() {
                    return nextIdx < getChildCount();
                }

                @Override
                public TreeNode nextElement() {
                    return getChildAt(nextIdx++);
                }
            };
        }

        @Override
        public String toString() {
            return "[" + dephth + "]" + name + "@" + superCrazyNumber;
        }
    }

    public WeirdNode root;
    private WeirdTree tree;

    public WeirdTreeFrame() {
        root = new WeirdNode(null, "Blubb");
        tree = new WeirdTree(root);
        add(tree);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(500,500);
        setVisible(true);
    }

    public void update() {
        for (int row = 0; row < tree.getRowCount(); row++) {
            ((DefaultTreeModel) tree.getModel()).nodeChanged((TreeNode)tree.getPathForRow(row).getLastPathComponent());
        }
    }


    public static void main(String[] args) {
        WeirdTreeFrame frame = new WeirdTreeFrame();
        Random rnd = new Random(41);

        for (long i = 0; ; i++) {
            for (WeirdNode node = frame.root; node != null; node = (WeirdNode)node.getChildAt(rnd.nextInt(3))) {
                node.superCrazyNumber++;
            }
            if (i % 1e7 == 0) {
                System.out.println("Update: " + i);
                frame.update();
            }
        }
    }
}

このupdate()方法では、適切なイベントを発生させて、表示されているすべてのノードが正しく更新されていることを確認しました。ご覧のとおり、計算中にいくつかの計算が並行して定期的に行われているので、ツリーラベル(構造ではなく)を更新したいと思います。

この方法の問題はupdate()、添付の図に示されているように、一部のノードが完全に間違ってラベル付けされていることです(ルートノードの場合は「[0] ...」、n番目のレベルの場合は「[n] ..」である必要があります)

ノードラベルの混同

これは何らかの競合状態だと思います。

4

3 に答える 3

11

これはおそらく、何かを変更したことを通知せずにツリーを更新していることを意味するため、再描画すると、レイアウトを再計算せずに再描画するだけです。モデルからDefaultTreeModel.nodeChanged(node)を呼び出して、実際に何かを変更したことをJTreeに通知する必要があります。そうすると、ノードのサイズの再計算が処理されます。または、適切なTreeModelListenerインターフェイス呼び出しを使用して、ツリーに何かが変更されたことを通知します。これを行うと、そのノードが適切にリレーアウトされます。

ツリーに適切に通知している場合、JLabelはそれ自体を再描画しますが、新しい文字列に必要なスペースを計算するためのレイアウトを再度実行していません。(repaint()ではなく)revalidate()を実行すると、レイアウトが再度実行され、新しい文字列に基づいて適切なサイズが再度計算され、文字列全体を囲むようにサイズが変更されます。

ツリーを操作してからupdateを呼び出すmain()メソッドのセクションは、WeirdFrameが構築されるときに、setVisible()の呼び出しによって実現されるため、スイングスレッドルールに違反しています。つまり、Swing Event Dispatcherスレッド以外のスレッドから描画するために使用されているモデルや、それに触れることはできません。TreeNodeを実装するかどうかとは関係ありません。

これに対する最も簡単な修正は、イベントディスパッチスレッドに戻すことです。そのようです:

public void update( final WeirdNode node, final long nextSuperCrazyNumber ) {
    SwingUtilities.invokeLater( new Runnable() {
        public void run() {
            node.superCrazyNumber = nextSuperCrazyNumber;
            ((DefaultTreeModel) tree.getModel()).nodeChanged(node);
        }
    });
}

public static void main(String[] args) {
    WeirdTreeFrame frame = new WeirdTreeFrame();
    Random rnd = new Random(41);

    for (long i = 0; ; i++) {
        for (WeirdNode node = frame.root; node != null; node  =(WeirdNode)node.getChildAt(rnd.nextInt(3))) {
            long superCrazyNumber = node.superCrazyNumber;
            frame.update( node, superCrazyNumber++ );
        }
    }
}

ここで、メインスレッドが自由に実行されていることに注目してください。ただし、メインスレッドはWeirdNodeに含まれるデータを直接操作していません。WeirdNodeのtoString()メソッドは、表示する文字列をレンダリングするために呼び出されるため、SwingThreadで呼び出されることに注意してください。別のスレッドからWeirdNode.superCrazyNumberに書き込む場合は、スレッドルールに違反しています。メインスレッドは、コピーを取得して計算を行い、SwingUtilities.invokeLater()を使用してSwingスレッドに更新をポストバックします。invokeAndWait()ではありません。非常に限られた状況を除いて、invokeAndWaitは必要ありません。また、ロックアップを引き起こす可能性があるため、回避するのが最善です。UIは毎秒モデルを反映する必要はなく、最終的にモデルを反映する必要があるだけなので、invokeLaterはあなたの友達です。ここで重要なのは私です

于 2012-04-29T20:41:00.273 に答える
2

ノード呼び出しメソッドnodeChangedを変更した後。

((DefaultTreeModel)tree).getModel().nodeChanged(node);
于 2014-04-24T10:49:12.207 に答える
1

DefaultTreeCellRendererJLabel便利な省略記号を追加するです。このでは、フレームのサイズを変更することで効果を確認できます。いくつかの選択肢があります。

  • 囲んでいるコンテナに適したレイアウトを使用してください。例では。を使用していますGridLayout

  • JTextField水平方向にスクロールできる、などのレンダラーのコンポーネントを使用します。

  • TreeSelectionListener隣接するテキストコンポーネントを更新するを追加します。

于 2012-04-29T18:19:04.863 に答える