概要

JComboBoxのドロップダウンリストとしてJListの代わりにJTableを使用します。

サンプルコード

class DropdownTableComboBox<E extends List<Object>> extends JComboBox<E> {
  protected final transient HighlightListener highlighter =
    new HighlightListener();

  protected final JTable table = new JTable() {
    @Override public Component prepareRenderer(
          TableCellRenderer renderer, int row, int column) {
      Component c = super.prepareRenderer(renderer, row, column);
      if (highlighter.isHighlightTableRow(row)) {
        c.setForeground(
          UIManager.getColor("Table.selectionForeground"));
        c.setBackground(
          UIManager.getColor("Table.selectionBackground").brighter());
      } else if (isRowSelected(row)) {
        c.setForeground(
          UIManager.getColor("Table.selectionForeground"));
        c.setBackground(
          UIManager.getColor("Table.selectionBackground"));
      } else {
        c.setForeground(UIManager.getColor("Table.foreground"));
        c.setBackground(UIManager.getColor("Table.background"));
      }
      return c;
    }

    @Override public void updateUI() {
      removeMouseListener(highlighter);
      removeMouseMotionListener(highlighter);
      super.updateUI();
      addMouseListener(highlighter);
      addMouseMotionListener(highlighter);
      getTableHeader().setReorderingAllowed(false);
    }
  };
  protected final List<E> list;

  protected DropdownTableComboBox(List<E> list, DefaultTableModel model) {
    super();
    this.list = list;
    table.setModel(model);
    list.forEach(this::addItem);
    // list.forEach(model::addRow);
    list.forEach(v -> model.addRow(v.toArray(new Object[0])));
  }

  @Override public void updateUI() {
    super.updateUI();
    EventQueue.invokeLater(() -> {
      setUI(new MetalComboBoxUI() {
        @Override protected ComboPopup createPopup() {
          return new ComboTablePopup(comboBox, table);
        }
      });
      setEditable(false);
    });
  }
  public List<Object> getSelectedRow() {
    return list.get(getSelectedIndex());
  }
}
View in GitHub: Java, Kotlin

解説

  • JListではなくJTableを使用するBasicComboPopupを作成
    • JTableなのでヘッダや複数列の設定などが可能
  • MetalComboBoxUI#createPopup()メソッドをオーバーライドしてJComboBoxのドロップダウンリストとして設定
class ComboTablePopup extends BasicComboPopup {
  private final JTable table;
  private final JScrollPane scroll;

  protected ComboTablePopup(JComboBox<?> combo, JTable table) {
    super(combo);
    this.table = table;

    ListSelectionModel sm = table.getSelectionModel();
    sm.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    sm.addListSelectionListener(e -> {
      combo.setSelectedIndex(table.getSelectedRow());
    });

    combo.addItemListener(e -> {
      if (e.getStateChange() == ItemEvent.SELECTED) {
        setRowSelection(combo.getSelectedIndex());
      }
    });

    table.addMouseListener(new MouseAdapter() {
      @Override public void mousePressed(MouseEvent e) {
        combo.setSelectedIndex(table.rowAtPoint(e.getPoint()));
        setVisible(false);
      }
    });

    scroll = new JScrollPane(table);
    setBorder(BorderFactory.createEmptyBorder());
  }

  @Override public void show() {
    if (isEnabled()) {
      Insets ins = scroll.getInsets();
      int tableh = table.getPreferredSize().height;
      int headerh = table.getTableHeader().getPreferredSize().height;
      scroll.setPreferredSize(new Dimension(
          240, tableh + headerh + ins.top + ins.bottom));
      super.removeAll();
      super.add(scroll);
      setRowSelection(comboBox.getSelectedIndex());
      super.show(comboBox, 0, comboBox.getBounds().height);
    }
  }

  private void setRowSelection(int index) {
    if (index != -1) {
      table.setRowSelectionInterval(index, index);
      table.scrollRectToVisible(table.getCellRect(index, 0, true));
    }
  }
}

参考リンク

コメント