• category: swing folder: TreeComboBox title: JComboBoxのItemをTree状に表示する tags: [JComboBox, TreeModel] author: aterai pubdate: 2011-07-11T15:16:42+09:00 description: JComboBoxのドロップダウンリストに表示するItemをTree状に配置します。 image: https://lh6.googleusercontent.com/-5GlQEjeLoH8/ThqUIL9b4UI/AAAAAAAAA_E/9h5dxYzSSm8/s800/TreeComboBox.png

概要

JComboBoxのドロップダウンリストに表示するItemTree状に配置します。

サンプルコード

#spandel
class TreeComboBox extends JComboBox {
#spanend
  public TreeComboBox() {
    super();
    setRenderer(new DefaultListCellRenderer() {
#spanadd
class TreeComboBox<E extends TreeNode> extends JComboBox<E> {
#spanend
  private boolean isNotSelectableIndex;
  private final Action up = new AbstractAction() {
    @Override public void actionPerformed(ActionEvent e) {
      int si = getSelectedIndex();
      for (int i = si - 1; i >= 0; i--) {
        if (getItemAt(i).isLeaf()) {
          setSelectedIndex(i);
          break;
        }
      }
    }
  };
#spanadd

#spanend
  private final Action down = new AbstractAction() {
    @Override public void actionPerformed(ActionEvent e) {
      int si = getSelectedIndex();
      for (int i = si + 1; i < getModel().getSize(); i++) {
        if (getItemAt(i).isLeaf()) {
          setSelectedIndex(i);
          break;
        }
      }
    }
  };
#spanadd

#spanend
  @Override public void updateUI() {
    super.updateUI();
    ListCellRenderer<? super E> renderer = getRenderer();
    setRenderer(new ListCellRenderer<E>() {
      @Override public Component getListCellRendererComponent(
          JList list, Object value, int index,
          JList<? extends E> list, E value, int index,
          boolean isSelected, boolean cellHasFocus) {
        JComponent c;
        if (value instanceof DefaultMutableTreeNode) {
        JLabel l = (JLabel) renderer.getListCellRendererComponent(
            list, value, index, isSelected, cellHasFocus);
        l.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
        if (index >= 0 && value instanceof DefaultMutableTreeNode) {
          DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
          int indent = 2 + (index < 0 ? 0 : (node.getPath().length - 2) * 16);
          if (node.isLeaf()) {
            c = (JComponent) super.getListCellRendererComponent(
                list, value, index, isSelected, cellHasFocus);
          } else {
            c = (JComponent) super.getListCellRendererComponent(
                list, value, index, false, false);
            JLabel l = (JLabel) c;
          int indent = Math.max(0, node.getLevel() - 1) * 16;
          l.setBorder(BorderFactory.createEmptyBorder(1, indent + 1, 1, 1));
          if (!value.isLeaf()) {
            l.setForeground(Color.WHITE);
            l.setBackground(Color.GRAY.darker());
          }
          c.setBorder(BorderFactory.createEmptyBorder(0, indent, 0, 0));
        } else {
          c = (JComponent) super.getListCellRendererComponent(
              list, value, index, isSelected, cellHasFocus);
        }
        return c;
        return l;
      }
    });
    Action up = new AbstractAction() {
      @Override public void actionPerformed(ActionEvent e) {
        int si = getSelectedIndex();
        for (int i = si - 1; i >= 0; i--) {
          Object o = getItemAt(i);
          if (o instanceof TreeNode && ((TreeNode) o).isLeaf()) {
            setSelectedIndex(i);
            break;
          }
        }
      }
    };
    Action down = new AbstractAction() {
      @Override public void actionPerformed(ActionEvent e) {
        int si = getSelectedIndex();
        for (int i = si + 1; i < getModel().getSize(); i++) {
          Object o = getItemAt(i);
          if (o instanceof TreeNode && ((TreeNode) o).isLeaf()) {
            setSelectedIndex(i);
            break;
          }
        }
      }
    };
    ActionMap am = getActionMap();
    am.put("selectPrevious3", up);
    am.put("selectNext3", down);
    InputMap im = getInputMap();
    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),      "selectPrevious3");
    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, 0),   "selectPrevious3");
    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),    "selectNext3");
    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, 0), "selectNext3");
    EventQueue.invokeLater(() -> {
      ActionMap am = getActionMap();
      am.put("selectPrevious3", up);
      am.put("selectNext3", down);
      InputMap im = getInputMap();
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "selectPrevious3");
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, 0), "selectPrevious3");
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "selectNext3");
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, 0), "selectNext3");
    });
  }
  private boolean isNotSelectableIndex = false;
#spanadd

#spanend
  @Override public void setPopupVisible(boolean v) {
    if (!v && isNotSelectableIndex) {
      isNotSelectableIndex = false;
    } else {
      super.setPopupVisible(v);
    }
  }
#spanadd

#spanend
  @Override public void setSelectedIndex(int index) {
    Object o = getItemAt(index);
    if (o instanceof TreeNode && !((TreeNode) o).isLeaf()) {
    TreeNode node = getItemAt(index);
    if (Objects.nonNull(node) && node.isLeaf()) {
      super.setSelectedIndex(index);
    } else {
      isNotSelectableIndex = true;
    } else {
      super.setSelectedIndex(index);
    }
  }
}
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、TreeModelから取得したTreeNodeJComboBoxItemとして、ドロップダウンリストに表示しています。

  • TreeNode#isLeaf()の場合だけ、選択可能
  • TreeNode#isLeaf()の場合のみ選択可能に設定
  • 子要素のインデントはBorderFactory.createEmptyBorder(0, indent, 0, 0)で設定
  • 0レベルのルートノードは非表示で第1レベルノードのインデントは0に設定
  • 2レベル以降の子ノードのインデントはBorderFactory.createEmptyBorder(1, indent + 1, 1, 1)で設定
  • -
  • TreeCellRendererを使用してノードアイコンなどを表示する場合のサンプル
#spanend
#spanadd
import java.awt.*;
#spanend
#spanadd
import java.util.Arrays;
#spanend
#spanadd
import java.util.Collections;
#spanend
#spanadd
import java.util.Enumeration;
#spanend
#spanadd
import java.util.Objects;
#spanend
#spanadd
import java.util.stream.Collectors;
#spanend
#spanadd
import javax.swing.*;
#spanend
#spanadd
import javax.swing.tree.DefaultMutableTreeNode;
#spanend
#spanadd
import javax.swing.tree.TreeCellRenderer;
#spanend
#spanadd

#spanend
#spanadd
public final class TreeComboBoxTest {
#spanend
  private Component makeUI() {
    JPanel p = new JPanel(new BorderLayout());
    p.add(new TreeComboBox(makeRoot()), BorderLayout.NORTH);
    p.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    return p;
  }
#spanadd

#spanend
  private static DefaultMutableTreeNode makeRoot() {
    return (DefaultMutableTreeNode) new JTree().getModel().getRoot();
  }
#spanadd

#spanend
  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
      } catch(Exception ex) {
        throw new IllegalArgumentException(ex);
      }
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new TreeComboBoxTest().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
#spanadd
}
#spanend
#spanadd

#spanend
#spanadd
class TreeComboBox extends JComboBox<DefaultMutableTreeNode> {
#spanend
  public TreeComboBox(DefaultMutableTreeNode root) {
    super();
    DefaultComboBoxModel<DefaultMutableTreeNode> m = new DefaultComboBoxModel<>();
    Collections.list((Enumeration<?>) root.preorderEnumeration()).stream()
        .filter(DefaultMutableTreeNode.class::isInstance)
        .map(DefaultMutableTreeNode.class::cast)
        .filter(n -> !n.isRoot())
        .forEach(m::addElement);
    setModel(m);
  }
#spanadd

#spanend
  @Override
  public void updateUI() {
    super.updateUI();
    JTree sampleTree = new JTree();
    TreeCellRenderer renderer = sampleTree.getCellRenderer();
    ListCellRenderer<? super DefaultMutableTreeNode> r = getRenderer();
    setRenderer((list, value, index, isSelected, cellHasFocus) -> {
      Component c = r.getListCellRendererComponent(
          list, value, index, isSelected, cellHasFocus);
      if (value == null) {
        return c;
      }
      if (index < 0) {
        String txt = Arrays.stream(value.getPath())
            .filter(DefaultMutableTreeNode.class::isInstance)
            .map(DefaultMutableTreeNode.class::cast)
            .filter(n -> !n.isRoot())
            .map(Objects::toString)
            .collect(Collectors.joining(" / "));
        ((JLabel) c).setText(txt);
        return c;
      } else {
        boolean leaf = value.isLeaf();
        JLabel l = (JLabel) renderer.getTreeCellRendererComponent(
            sampleTree, value, isSelected, true, leaf, index, false);
        int childIndent = UIManager.getInt("Tree.leftChildIndent") +
                          UIManager.getInt("Tree.rightChildIndent");
        int indent = Math.max(0, value.getLevel() - 1) * childIndent;
        l.setBorder(BorderFactory.createEmptyBorder(1, indent + 1, 1, 1));
        return l;
      }
    });
  }
#spanadd
}
#spanend
#spanadd

参考リンク

コメント