Swing/TreeNodeExpandCollapseAnimations のバックアップ(No.6)
- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- 現在との差分 - 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) {
Object o = e.getPath().getLastPathComponent();
if (o instanceof DefaultMutableTreeNode) {
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);
}
}
@Override public void treeWillCollapse(TreeExpansionEvent e)
throws ExpandVetoException {
Object c = e.getPath().getLastPathComponent();
if (c instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode p = (DefaultMutableTreeNode) o;
List<DefaultMutableTreeNode> list = getTreeNodes(p);
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();
}
Windows 10
+Java 1.8.0_422
で作成したexample.jar
で+
アイコンをクリックしてノード展開を実行すると初回のみアニメーションが開始されない- ノードのラベルをダブルクリックで展開すると問題なく実行可能で、それ以降は
+
アイコンをクリックしてノード展開してもアニメーション可能 - 自宅の
Windows 10
+Java 1.8.0_422
環境が壊れているだけかも?
- ノードのラベルをダブルクリックで展開すると問題なく実行可能で、それ以降は
OS | JDK | |
× | Windows 10 | Java 1.8.0_422 |
〇 | Windows 11 | OpenJDK Runtime Environment Corretto-8.422.05.1 (build 1.8.0_422-b05) |
〇 | Windows 11 | OpenJDK Runtime Environment (Temurin)(build 1.8.0_422-b05) |
〇 | Windows 11 | OpenJDK Runtime Environment Corretto-21.0.4.7.1 (build 21.0.4+7-LTS) |
参考リンク
- JTreeのノードを名前で検索して表示のフィルタリングを行う
- JTreeで親ノードが展開されたときに子ノードの選択状態を変更する
- JTableで行の追加、削除アニメーション
- JTreeのノードを折り畳み不可に設定する
- JTreeのノード追加、削除