カスタムモデルとカスタムレンダラーを備えたJTreeがあります。複数の子ノードを持つノードを展開すると、すべての子ノードが同じ長方形に描画されるため、最後のノードのみが表示されることを除いて、これらは正常に機能しているようです。子ノードを反復処理してコンソールに出力することはできますが、UIが異なります。
これはUIのスケッチです。
// for 1 child node:
ROOT
-- <rendered node>
//for 2 child nodes:
ROOT
|
-- <rendered node - the data of the 2nd node>
//for 3 child nodes:
ROOT
|
|
-- <rendered node - the data of the 3rd node>
BasicTreeUIがgetPathBounds()を使用して、特定のノードの境界を決定していることがわかりました。これが私がそれをチェックする方法です:
doStuffWithTree(JTree tree) {
tree.expandNode(0);
for (int i = 0; i < tree.getRowCount(); i++) {
TreePath tp = tree.getPathForRow(i);
tree.makeVisible(tp);
System.out.println("Bounds of row " + i + ": " + tree.getPathBounds(tp));
}
}
このチェックにより、ノード0を除くすべてのノードの境界が同じであることがわかります。
ツリーを拡張するいくつかのトリックを試しました。たとえば、最初からすべてのノードを拡張します。その後、最後から始めます-それらはすべて同じ効果があります。
最後に、質問:この動作を修正するためにどのクラスを変更できますか?既知の回避策はありますか?
アップデート
どうやら、TreeNodeの実装で何かを修正する必要があります。また、自分で修正する必要があります。カスタムモデルはありませんでしたが、カスタムのTreeNodeがありました。SSCCEは次のとおりです。
package com.example.jtree;
//lots of obvious imports
public class JTreeBoundsExample extends JFrame {
public static void main(String... args) {
JTreeBoundsExample m = new JTreeBoundsExample();
m.setSize(275, 300);
m.setDefaultCloseOperation(EXIT_ON_CLOSE);
m.setVisible(true);
}
public JTreeBoundsExample() {
ValueTreeNode root = new ValueTreeNode(null, "root");
ValueTreeNode category = new ValueTreeNode(root, "category");
category.put("k1", new ValueTreeNode(category, "v1"));
category.put("k2", new ValueTreeNode(category, "v2"));
category.put("k3", new ValueTreeNode(category, "v3"));
root.put("category", category);
JTree tree = new JTree(root);
tree.setCellRenderer(new ValueCellRenderer());
tree.setRootVisible(false);
tree.expandRow(0);
for (int i = 0; i < tree.getRowCount(); i++) {
TreePath tp = tree.getPathForRow(i);
tree.makeVisible(tp);
System.out.println("Bounds of row " + i + ": " + tree.getPathBounds(tp));
}
Container content = getContentPane();
content.add(new JScrollPane(tree), BorderLayout.CENTER);
}
private static class ValueTreeNode extends HashMap<String, ValueTreeNode> implements TreeNode {
private List<String> keys = new ArrayList<String>();
private TreeNode parent;
private String value;
public ValueTreeNode(TreeNode parent, String value) {
this.parent = parent;
this.value = value;
}
@Override
public ValueTreeNode put(String key, ValueTreeNode val) {
ValueTreeNode old = super.put(key, val);
if (old == null) {
sortKeys();
}
return old;
}
private void sortKeys() {
keys.clear();
keys.addAll(keySet());
Collections.sort(keys);
}
@Override
public Enumeration children() {
final Iterator<String> it = keys.iterator();
return new Enumeration() {
@Override
public boolean hasMoreElements() {
return it.hasNext();
}
@Override
public Object nextElement() {
return it.next();
}
};
}
@Override
public boolean getAllowsChildren() {
return true;
}
@Override
public TreeNode getChildAt(int pos) {
return get(keys.get(pos));
}
@Override
public int getChildCount() {
return size();
}
@Override
public int getIndex(TreeNode arg0) {
return -1;
}
@Override
public TreeNode getParent() {
return parent;
}
@Override
public boolean isLeaf() {
return size() == 0;
}
}
private static class ValueCellRenderer extends DefaultTreeCellRenderer {
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
if (value instanceof ValueTreeNode) {
ValueTreeNode vtn = (ValueTreeNode) value;
setText(vtn.value);
}
return this;
}
}
}