更改后,JTree convertValueToText返回被截断

Zul*_*lan 2 java swing jtree

我有一个实现的自定义JTree实现convertValueToText.这种实现取决于某些全球状态.如果返回的字符串比以前返回的字符串更长(实际上我认为更宽,就像在像素中触发它),文本将被截断并用"..."填充.当重绘是由(de)选择元素或repaint树上引起时,这是真的.

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());
                }
            }
        });
Run Code Online (Sandbox Code Playgroud)

似乎工作正常.

更新:

这是一个最小的例子:

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();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

通过该update()方法,我尝试触发相应的事件以确保所有可见节点都正确更新.正如您所看到的,计算期间并行和周期性地发生了一些计算,我想更新树标签(而不是结构).

这个update()方法的问题是有些节点完全错误,你可以在附图中看到(对于根节点应该是"[0] ...",对于第n级应该是"[n] ..")

混合节点标签

我认为这是一些竞争条件.

chu*_*ubs 10

这可能意味着您正在更新树而不告诉它您更改了某些内容,因此在重绘它时只需重新绘制它而不重新计算布局.您必须从模型中调用DefaultTreeModel.nodeChanged(node)来通知JTree您实际修改了某些内容然后它将处理重新计算节点的大小.或使用正确的TreeModelListener接口调用来通知Tree已更改的内容.如果你这样做,它将正确地重新传输该节点.

如果您正确地通知树JLabel正在重绘,但它没有再次执行布局来计算新字符串需要多少空间.如果你重新验证()它(与repaint()相反)它应该再次执行布局,它将根据新字符串再次计算其首选大小,并调整自身大小以包围整个字符串.

main()方法中操作树然后调用update的部分违反了swing线程规则,因为当构造WeirdFrame时,它通过调用setVisible()来实现.这意味着除了Swing Event Dispatcher线程之外,您无法触摸它或任何用于从任何线程中绘制的模型.它与您是否实现TreeNode没有任何关系.

最简单的解决方法是将其推迟到Event Dispatch线程.像这样:

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++ );
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在注意主线程如何不受限制地运行,但它不直接操作WeirdNode中包含的数据.记得调用WeirdNode的toString()方法来渲染要显示的字符串,以便在SwingThread上调用它.如果从另一个线程写入WeirdNode.superCrazyNumber,则表示您违反了线程规则.主线程获取副本对其进行一些计算,然后使用SwingUtilities.invokeLater()NOT invokeAndWait()将更新发布回Swing Thread.你不需要invokeAndWait,除非是非常有限的情况,它有可能导致锁定,所以最好避免它.您的UI不必反映模型,它最终只能反映它,因此invokeLater是您的朋友.这里的关键是我没有像以前的代码那样在主线程上进行写操作,而且我当然可以在各个节点上调用update而不必使用shotgun更新.