• category: swing folder: ListComboBoxEditor title: ComboBoxEditorとして複数アイテムが表示可能なJListを使用する tags: [JList, JPopupMenu, JComboBox, JScrollPane, JToggleButton] author: aterai pubdate: 2022-07-04T02:53:37+09:00 description: ComboBoxEditorとして複数アイテムが表示やスクロールが可能なニュースペーパースタイルのJListを使用します。 image: https://drive.google.com/uc?id=1Wf8pV3xZzSyuHrmifdUcrSY09lCTGf2R

概要

ComboBoxEditorとして複数アイテムが表示やスクロールが可能なニュースペーパースタイルのJListを使用します。

サンプルコード

ListModel<ListItem> model = makeModel();
JList<ListItem> list = new NewspaperStyleList<>(model);
JScrollPane scroll = new JScrollPane(list) {
  @Override public Dimension getPreferredSize() {
    Dimension d = super.getPreferredSize();
    d.width = 62 * 3 + 10;
    d.height = 32;
    return d;
  }
};
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
scroll.setBorder(BorderFactory.createEmptyBorder());
scroll.setViewportBorder(BorderFactory.createEmptyBorder());

JList<ListItem> list2 = new NewspaperStyleList<>(model);
JScrollPane scroll2 = new JScrollPane(list2) {
  @Override public Dimension getPreferredSize() {
    Dimension d = super.getPreferredSize();
    d.width = 62 * 4 + 10;
    return d;
  }
};
scroll2.setBorder(BorderFactory.createEmptyBorder());
scroll2.setViewportBorder(BorderFactory.createEmptyBorder());

JPopupMenu popup = new JPopupMenu();
popup.setLayout(new BorderLayout());
list2.addMouseListener(new MouseAdapter() {
  @Override public void mouseClicked(MouseEvent e) {
    if (popup.isVisible() && e.getClickCount() >= 2) {
      popup.setVisible(false);
    }
  }
});
popup.add(scroll2);
popup.setBorder(BorderFactory.createLineBorder(Color.GRAY));

JToggleButton dropDown = new JToggleButton(new DropDownArrowIcon());
popup.addPopupMenuListener(new PopupMenuListener() {
  @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
    list2.setSelectedIndex(list.getSelectedIndex());
    EventQueue.invokeLater(popup::pack);
  }

  @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
    dropDown.setSelected(false);
    list.requestFocusInWindow();
    int i = list2.getSelectedIndex();
    if (i >= 0) {
      list.setSelectedIndex(i);
      list.scrollRectToVisible(list.getCellBounds(i, i));
    }
  }

  @Override public void popupMenuCanceled(PopupMenuEvent e) {
    popupMenuWillBecomeInvisible(e);
  }
});

dropDown.setBorder(BorderFactory.createEmptyBorder());
dropDown.setContentAreaFilled(false);
dropDown.setFocusPainted(false);
dropDown.setFocusable(false);
dropDown.addItemListener(e -> {
  AbstractButton b = (AbstractButton) e.getItemSelectable();
  if (e.getStateChange() == ItemEvent.SELECTED) {
    popup.show(b, -scroll.getWidth(), b.getHeight());
  }
});

JScrollBar verticalScrollBar = scroll.getVerticalScrollBar();
JPanel verticalBox = new JPanel(new BorderLayout()) {
  @Override public Dimension getPreferredSize() {
    Dimension d = super.getPreferredSize();
    d.height = 32 + 5 + 5;
    return d;
  }
};
verticalBox.setOpaque(false);
verticalBox.add(verticalScrollBar);
verticalBox.add(dropDown, BorderLayout.SOUTH);

JPanel panel = new JPanel(new BorderLayout(0, 0));
panel.add(scroll);
panel.add(verticalBox, BorderLayout.EAST);
panel.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
View in GitHub: Java, Kotlin

解説

  • JList + JPopupMenu
    • エディタ部分にニュースペーパースタイルで複数アイテムが表示可能なJList、その親JScrollPaneから縦JScrollBar、ドロップダウン表示用のJToggleButtonを組み合わせてJComboBox風のコンポーネントを作成
    • ドロップダウンリストはJPopupMenuで作成し、その子コンポーネントとしてエディタ部分のJListとは別のJListを追加
    • MouseListenerPopupMenuListenerでドロップダウンリスト内のJListの選択状態変更をエディタ部分のJListに反映するよう設定
  • JComboBox + ComboBoxEditor
ListModel<ListItem> model = makeModel();
JList<ListItem> list = new NewspaperStyleList<>(model);
JScrollPane scroll = new JScrollPane(list) {
  @Override public Dimension getPreferredSize() {
    Dimension d = super.getPreferredSize();
    d.width = 62 * 4;
    d.height = 40;
    return d;
  }
};
scroll.setBorder(BorderFactory.createEmptyBorder());
scroll.setViewportBorder(BorderFactory.createEmptyBorder());
DefaultComboBoxModel<ListItem> cm = new DefaultComboBoxModel<>();
for (int i = 0; i < model.getSize(); i++) {
  cm.addElement(model.getElementAt(i));
}
JComboBox<ListItem> combo = new JComboBox<ListItem>(cm) {
  @Override public void updateUI() {
    super.updateUI();
    setMaximumRowCount(4);
    setPrototypeDisplayValue(new ListItem("red", new ColorIcon(Color.RED)));
    EventQueue.invokeLater(() -> {
      ComboPopup popup = (ComboPopup) getAccessibleContext().getAccessibleChild(0);
      JList<?> lst = popup.getList();
      lst.setLayoutOrientation(JList.HORIZONTAL_WRAP);
      lst.setVisibleRowCount(0);
      lst.setFixedCellWidth(62);
      lst.setFixedCellHeight(40);
      lst.setOpaque(true);
      lst.setBackground(new Color(0x32_32_32));
      lst.setForeground(Color.WHITE);
    });
  }
};
combo.setRenderer(new ListItemListCellRenderer<>());
combo.setEditable(true);
combo.setEditor(new ComboBoxEditor() {
  @Override public Component getEditorComponent() {
    return scroll;
  }

  @Override public void setItem(Object anObject) {
    combo.setSelectedIndex(list.getSelectedIndex());
  }

  @Override public Object getItem() {
    return list.getSelectedValue();
  }

  @Override public void selectAll() {
    // System.out.println("selectAll");
  }

  @Override public void addActionListener(ActionListener l) {
    // System.out.println("addActionListener");
  }

  @Override public void removeActionListener(ActionListener l) {
    // System.out.println("removeActionListener");
  }
});

参考リンク

コメント