Swing/TreeNodeExpandCollapseAnimations のバックアップ(No.2)
- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- 現在との差分 - Visual を表示
- ソース を表示
- Swing/TreeNodeExpandCollapseAnimations へ行く。
- category: swing folder: TreeNodeExpandCollapseAnimations title: JTreeにノード展開、折り畳みアニメーションを実装する tags: [JTree, Animation, TreeWillExpandListener] author: aterai pubdate: 2024-07-22T02:27:36+09:00 description: JTreeのノード展開、折り畳みでその子ノードの高さを増減するアニメーションを実行します。 image: https://drive.google.com/uc?id=1hHsf6k4Zt-UnrvuI-GIZWmu8NDCd6Yii
概要
JTree
のノード展開、折り畳みでその子ノードの高さを増減するアニメーションを実行します。
Screenshot
Advertisement
サンプルコード
JTree tree = new JTree() {
@Override public void updateUI() {
super.updateUI();
setRowHeight(-1);
setCellRenderer(new HeightTreeCellRenderer());
}
};
tree.addTreeWillExpandListener(new TreeWillExpandListener() {
@Override public void treeWillExpand(TreeExpansionEvent e) {
JTree t = (JTree) e.getSource();
TreePath anchor = t.getAnchorSelectionPath();
TreePath lead = t.getLeadSelectionPath();
TreePath path = e.getPath();
Object o = path.getLastPathComponent();
if (o instanceof DefaultMutableTreeNode && t.isPathSelected(path)) {
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) o;
List<DefaultMutableTreeNode> list = getTreeNodes(parent);
parent.setUserObject(makeUserObject(parent, END_HEIGHT));
list.forEach(n -> n.setUserObject(makeUserObject(n, START_HEIGHT)));
startExpandTimer(e, list);
TreePath[] paths = list
.stream()
.map(TreePath::new)
.toArray(TreePath[]::new);
t.addSelectionPaths(paths);
t.setAnchorSelectionPath(anchor);
t.setLeadSelectionPath(lead);
}
}
@Override public void treeWillCollapse(TreeExpansionEvent e)
throws ExpandVetoException {
TreePath path = e.getPath();
Object o = path.getLastPathComponent();
if (o instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode root = (DefaultMutableTreeNode) o;
List<DefaultMutableTreeNode> list = getTreeNodes(root);
boolean b = list.stream()
.anyMatch(n -> {
Object obj = n.getUserObject();
return obj instanceof SizeNode
&& ((SizeNode) obj).height == END_HEIGHT;
});
if (b) {
startCollapseTimer(e, list);
throw new ExpandVetoException(e);
}
}
}
});
class HeightTreeCellRenderer extends DefaultTreeCellRenderer {
@Override public Component getTreeCellRendererComponent(
JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
Component c = super.getTreeCellRendererComponent(
tree, value, selected, expanded, leaf, row, hasFocus);
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
Object uo = node.getUserObject();
if (c instanceof JLabel && uo instanceof SizeNode) {
JLabel l = (JLabel) c;
SizeNode n = (SizeNode) uo;
l.setPreferredSize(null); // reset prev preferred size
l.setText(n.label); // recalculate preferred size
Dimension d = l.getPreferredSize();
d.height = n.height;
l.setPreferredSize(d);
}
return c;
}
}
class SizeNode {
public final String label;
public final int height;
protected SizeNode(String label, int height) {
this.label = label;
this.height = height;
}
@Override public String toString() {
return label;
}
}
View in GitHub: Java, Kotlin解説
JTree#setRowHeight(-1)
を設定して各表示行の高さを固定サイズではなくセルレンダラーの推奨サイズの高さを使用するよう設定- セルレンダラーの推奨サイズを
DefaultMutableTreeNode
のユーザーオブジェクトで指定した高さを指定することで展開・折り畳み中のアニメーションを実行- ユーザーオブジェクトの設定は
DefaultMutableTreeNode#setUserObject(obj)
ではなくTreeModel#valueForPathChanged(path, obj)
を使用しないとTreeModelListener
などにノード更新イベントが伝わらない JLabel#setPreferredSize(null)
を実行してからノードの文字列を設定しないと推奨サイズの幅が正しく取得できないJTree
のノードサイズキャッシュが影響している?
- ユーザーオブジェクトの設定は
JTree
にTreeWillExpandListener
を追加してノードの展開・折り畳み前にその子ノードの高さを増減するアニメーションを実行TreeWillExpandListener#treeWillExpand(...)
で展開前に子ノードすべての高さを縮小してから、Timer
をスタートして段階的に元の高さに戻すことでアニメーションを表現TreeWillExpandListener#treeWillCollapse(...)
で子ノードの高さが元の高さの場合は、一旦throw new ExpandVetoException(...)
で折り畳みを中止してからTimer
をスタートして段階的に高さを縮小するアニメーションを実行し、一定の高さまで縮小されたらJTree#collapsePath(TreePath)
で再度アニメーションなしの折り畳みを実行
private static void startExpandTimer(
TreeExpansionEvent e, List<DefaultMutableTreeNode> list) {
JTree tree = (JTree) e.getSource();
TreeModel model = tree.getModel();
AtomicInteger height = new AtomicInteger(START_HEIGHT);
new Timer(DELAY, ev -> {
int h = height.getAndIncrement();
if (h <= END_HEIGHT) {
list.forEach(n -> {
Object uo = makeUserObject(n, h);
model.valueForPathChanged(new TreePath(n.getPath()), uo);
});
} else {
((Timer) ev.getSource()).stop();
}
}).start();
}
private static void startCollapseTimer(
TreeExpansionEvent e, List<DefaultMutableTreeNode> list) {
JTree tree = (JTree) e.getSource();
TreePath path = e.getPath();
TreeModel model = tree.getModel();
AtomicInteger height = new AtomicInteger(END_HEIGHT);
new Timer(DELAY, ev -> {
int h = height.getAndDecrement();
if (h >= START_HEIGHT) {
list.forEach(n -> {
Object uo = makeUserObject(n, h);
model.valueForPathChanged(new TreePath(n.getPath()), uo);
});
} else {
((Timer) ev.getSource()).stop();
tree.collapsePath(path);
}
}).start();
}
参考リンク
- JTreeのノードを名前で検索して表示のフィルタリングを行う
- JTreeで親ノードが展開されたときに子ノードの選択状態を変更する
- JTableで行の追加、削除アニメーション
- JTreeのノードを折り畳み不可に設定する
- JTreeのノード追加、削除