Swing/CheckBoxNodeEditor のバックアップ差分(No.22)
- バックアップ一覧
- 現在との差分 を表示
- 現在との差分 - Visual を表示
- ソース を表示
- バックアップ を表示
- Swing/CheckBoxNodeEditor へ行く。
- 1 (2012-02-06 (月) 14:38:59)
- 2 (2012-02-07 (火) 13:58:43)
- 3 (2012-03-23 (金) 17:43:32)
- 4 (2012-05-10 (木) 10:52:04)
- 5 (2012-05-11 (金) 16:50:47)
- 6 (2012-12-13 (木) 15:41:34)
- 7 (2013-08-17 (土) 15:37:51)
- 8 (2014-02-18 (火) 15:23:02)
- 9 (2014-03-04 (火) 02:49:03)
- 10 (2014-03-04 (火) 11:06:52)
- 11 (2014-07-04 (金) 15:12:37)
- 12 (2014-08-15 (金) 19:46:41)
- 13 (2014-09-17 (水) 13:10:51)
- 14 (2014-10-22 (水) 00:52:41)
- 15 (2014-11-26 (水) 02:34:41)
- 16 (2015-02-06 (金) 11:59:40)
- 17 (2015-04-02 (木) 15:09:47)
- 18 (2017-02-22 (水) 13:58:44)
- 19 (2017-11-02 (木) 15:34:40)
- 20 (2017-12-28 (木) 22:28:33)
- 21 (2018-02-24 (土) 19:51:30)
- 22 (2019-05-22 (水) 19:35:38)
- 23 (2019-12-18 (水) 19:52:49)
- 24 (2021-06-19 (土) 17:20:17)
- 25 (2022-08-20 (土) 22:15:25)
- 追加された行はこの色です。
- 削除された行はこの色です。
--- category: swing folder: CheckBoxNodeEditor title: JTreeのすべてのノードにJCheckBoxを追加する tags: [JTree, JCheckBox, TreeCellRenderer, TreeCellEditor, TreeModelListener, Icon] author: aterai pubdate: 2012-02-06T14:38:59+09:00 description: JTreeのすべてのノードに編集可能なJCheckBoxを追加します。 image: https://lh4.googleusercontent.com/-DK6aW3VNikg/TygxL3j8UoI/AAAAAAAABIw/6_9FyPe4v7U/s800/CheckBoxNodeEditor.png hreflang: href: http://java-swing-tips.blogspot.com/2012/02/jcheckbox-node-jtree.html href: https://java-swing-tips.blogspot.com/2012/02/jcheckbox-node-jtree.html lang: en --- * 概要 [#summary] `JTree`のすべてのノードに編集可能な`JCheckBox`を追加します。 #download(https://lh4.googleusercontent.com/-DK6aW3VNikg/TygxL3j8UoI/AAAAAAAABIw/6_9FyPe4v7U/s800/CheckBoxNodeEditor.png) * サンプルコード [#sourcecode] #code(link){{ class CheckBoxNodeEditor extends TriStateCheckBox implements TreeCellEditor { private DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer(); private final JPanel panel = new JPanel(new BorderLayout()); private String str = null; public CheckBoxNodeEditor() { super(); this.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { //System.out.println("actionPerformed: stopCellEditing"); stopCellEditing(); } }); panel.setFocusable(false); panel.setRequestFocusEnabled(false); panel.setOpaque(false); panel.add(this, BorderLayout.WEST); this.setOpaque(false); } @Override public Component getTreeCellEditorComponent( JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { JLabel l = (JLabel) renderer.getTreeCellRendererComponent( tree, value, true, expanded, leaf, row, true); l.setFont(tree.getFont()); if (value instanceof DefaultMutableTreeNode) { this.setEnabled(tree.isEnabled()); this.setFont(tree.getFont()); Object userObject = ((DefaultMutableTreeNode) value).getUserObject(); if (userObject instanceof CheckBoxNode) { CheckBoxNode node = (CheckBoxNode) userObject; if (node.status == Status.INDETERMINATE) { setIcon(new IndeterminateIcon()); } else { setIcon(null); } l.setText(node.label); setSelected(node.status == Status.SELECTED); str = node.label; } //panel.add(this, BorderLayout.WEST); panel.add(l); return panel; } return l; } @Override public Object getCellEditorValue() { return new CheckBoxNode(str, isSelected() ? Status.SELECTED : Status.DESELECTED); } @Override public boolean isCellEditable(EventObject e) { if (e instanceof MouseEvent && e.getSource() instanceof JTree) { MouseEvent me = (MouseEvent) e; JTree tree = (JTree) e.getSource(); TreePath path = tree.getPathForLocation(me.getX(), me.getY()); Rectangle r = tree.getPathBounds(path); if (r == null) { return false; } Dimension d = getPreferredSize(); r.setSize(new Dimension(d.width, r.height)); if (r.contains(me.getPoint())) { return true; } } return false; } @Override public void updateUI() { super.updateUI(); setName("Tree.cellEditor"); if (panel != null) { //panel.removeAll(); //??? Change to Nimbus LnF, JDK 1.6.0 panel.updateUI(); //panel.add(this, BorderLayout.WEST); } //???#1: JDK 1.6.0 bug??? @see 1.7.0 DefaultTreeCellRenderer#updateUI() //if (System.getProperty("java.version").startsWith("1.6.0")) { // renderer = new DefaultTreeCellRenderer(); //} } //... }} * 解説 [#explanation] 上記のサンプルでは、`JCheckBox`を継承する`TreeCellEditor`、`TreeCellRenderer`を作成し、`TreeCellEditor#getTreeCellEditorComponent(...)`などは、この`JCheckBox`、`JLabel`(文字列、アイコン)などを含む`JPanel`を生成して返しています。 マウスクリックなどでノードのチェック状態が変更されたら子ノードと親ノードの選択状態も更新する必要があるので、`TreeModelListener`をモデルに設定してその監視を行っています。 ---- `JPanel`を継承する`TreeCellEditor`、`TreeCellRenderer`でも、`JDK 1.7.0`、`JDK 1.6.0_30`などでは、問題なく動作するようです。 #code{{ class CheckBoxNodeRenderer extends JPanel implements TreeCellRenderer { private DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer(); private final TriStateCheckBox check = new TriStateCheckBox(); public CheckBoxNodeRenderer() { super(new BorderLayout()); String uiName = getUI().getClass().getName(); if (uiName.contains("Synth") && System.getProperty("java.version").startsWith("1.7.0")) { System.out.println("XXX: FocusBorder bug?, JDK 1.7.0, Nimbus start LnF"); renderer.setBackgroundSelectionColor(new Color(0x0, true)); } setFocusable(false); setRequestFocusEnabled(false); setOpaque(false); add(check, BorderLayout.WEST); check.setOpaque(false); } @Override public Component getTreeCellRendererComponent( JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { JLabel l = (JLabel) renderer.getTreeCellRendererComponent( tree, value, selected, expanded, leaf, row, hasFocus); l.setFont(tree.getFont()); if (value instanceof DefaultMutableTreeNode) { check.setEnabled(tree.isEnabled()); check.setFont(tree.getFont()); Object userObject = ((DefaultMutableTreeNode) value).getUserObject(); if (userObject instanceof CheckBoxNode) { CheckBoxNode node = (CheckBoxNode) userObject; if (node.status == Status.INDETERMINATE) { check.setIcon(new IndeterminateIcon()); } else { check.setIcon(null); } l.setText(node.label); check.setSelected(node.status == Status.SELECTED); } add(l); return this; } return l; } @Override public void updateUI() { super.updateUI(); if (check != null) { //removeAll(); //??? Change to Nimbus LnF, JDK 1.6.0 check.updateUI(); //add(check, BorderLayout.WEST); } setName("Tree.cellRenderer"); //???#1: JDK 1.6.0 bug??? @see 1.7.0 DefaultTreeCellRenderer#updateUI() //if (System.getProperty("java.version").startsWith("1.6.0")) { // renderer = new DefaultTreeCellRenderer(); //} } } }} ---- - `JDK 1.6.0`で`LookAndFeel`を`Nimbus`などに変更すると、セルエディタなどが更新されず、表示が不正になる場合があるので、`JTree#updateUI()`を以下のようにオーバーライドして回避 #code{{ JTree tree = new JTree() { @Override public void updateUI() { setCellRenderer(null); setCellEditor(null); super.updateUI(); //???#1: JDK 1.6.0 bug??? Nimbus LnF setCellRenderer(new CheckBoxNodeRenderer()); setCellEditor(new CheckBoxNodeEditor()); } }; }} - `JDK 1.7.0`で、初期`LookAndFeel`を`Nimbus`にすると、ノードにフォーカスがある場合のグラデーション描画がノードの背景色で塗りつぶされてしまう?のを、以下のようにして回避 -- &ref(https://lh3.googleusercontent.com/-DQgyx52YcsQ/T6sfFSWGIpI/AAAAAAAABMc/jAx8XeuMeWI/s800/CheckBoxNodeEditor1.png); #code{{ public CheckBoxNodeRenderer() { super(); String uiName = getUI().getClass().getName(); if (uiName.contains("Synth") && System.getProperty("java.version").startsWith("1.7.0")) { System.out.println("XXX: FocusBorder bug?, JDK 1.7.0, Nimbus start LnF"); renderer.setBackgroundSelectionColor(new Color(0x0, true)); } //... }} - %%`JDK 1.7.0`で、ノードのチェックボックスをクリックしても、初回だけ反応しない%% `1.7.0_60`で修正されている -- `JDK 1.6.0_30`などは問題なし -- `TreeCellEditor#isCellEditable()`をオーバーライドして、初回のみセルエディタのサイズを以下のように設定 -- `JDK 1.8.0`では、修正されている -- [https://bugs.openjdk.java.net/browse/JDK-8023474 Bug ID: JDK-8023474 First mousepress doesn't start editing in JTree] #code{{ if (isFirstTime && System.getProperty("java.version").startsWith("1.7.0")) { System.out.println("XXX: Java 7, only on first run\n" + getBounds()); setBounds(new Rectangle(0, 0, d.width, r.height)); } }} * 参考リンク [#reference] - [[JTreeの葉ノードをJCheckBoxにする>Swing/CheckBoxNodeTree]] - [[JCheckBoxに不定状態のアイコンを追加する>Swing/TriStateCheckBox]] - [[JCheckBox付きJTreeでディレクトリ構造を表示>Swing/FileSystemTreeWithCheckBox]] * コメント [#comment] #comment - 親ノードまでではなく、ルートノードまで不定状態の変更を行うように修正。 -- &user(aterai); &new{2012-03-23 (金) 17:43:32}; #comment