• category: swing folder: HorizontalWrapComboPopup title: JComboBoxのドロップダウンリストでセル配置をニュースペーパー・スタイルに設定する tags: [JComboBox, JList, ComboPopup] author: aterai pubdate: 2020-03-23T18:45:14+09:00 description: JComboBoxのドロップダウンリストに配置されるJListのセルレイアウト方法をニュースペーパー・スタイルに変更します。 image: https://drive.google.com/uc?id=1LMv0w83y7RJ0-0V28KBL2DIppbJ9EM1P

概要

JComboBoxのドロップダウンリストに配置されるJListのセルレイアウト方法をニュースペーパー・スタイルに変更します。

サンプルコード

private static JComboBox<Icon> makeComboBox1(ComboBoxModel<Icon> model, Icon proto) {
  return new JComboBox<Icon>(model) {
    @Override public Dimension getPreferredSize() {
      Insets i = getInsets();
      int w = proto.getIconWidth();
      int h = proto.getIconHeight();
      return new Dimension(w * 3 + i.left + i.right, h + i.top + i.bottom);
    }

    @Override public void updateUI() {
      super.updateUI();
      setMaximumRowCount(3);
      setPrototypeDisplayValue(proto);

      ComboPopup popup = (ComboPopup) getAccessibleContext().getAccessibleChild(0);
      JList<?> list = popup.getList();
      list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
      list.setVisibleRowCount(3);
      list.setFixedCellWidth(proto.getIconWidth());
      list.setFixedCellHeight(proto.getIconHeight());
    }
  };
}
View in GitHub: Java, Kotlin

解説

  • PreferredSize
    • JComboBox#getPreferredSize()をオーバーライドしてJComboBoxの幅をセルサイズの3倍になるよう変更
    • ComboPopup#getList()でドロップダウンリストに配置されるJListを取得してセルの配置方法をHORIZONTAL_WRAPに変更
      • JListの下側に余計な余白が生成されてしまう?
    • JComboBox#setMaximumRowCount(3)JList#setVisibleRowCount(3)3行のセルを表示するよう変更
      • 片方だけ設定するとスクロールバーが表示されたり、余計な余白ができる場合がある
    • JComboBox#setPrototypeDisplayValue(...)JList#setFixedCellWidth(...)JList#setFixedCellHeight(...)でセルサイズを変更
      • JComboBox#setPrototypeDisplayValue(...)のみ指定する場合、セルの右に余分な余白が生成される?
  • PopupMenuListener
    • JComboBox#getPreferredSize()をオーバーライドしてJComboBoxの幅をセルサイズと矢印ボタンの合計になるよう変更
      • このサンプルで使用している矢印ボタンの幅は20px固定
    • PopupMenuListenerを追加してドロップダウンリストを開く直前だけJComboBoxの幅をセルサイズの3倍になるよう変更
    • JListの下側に余計な余白が生成されてしまうことを防ぐためListCellRendererを変更
private static JComboBox<Icon> makeComboBox2(ComboBoxModel<Icon> model, Icon proto) {
  JComboBox<Icon> combo = new JComboBox<Icon>(model) {
    @Override public Dimension getPreferredSize() {
      Insets i = getInsets();
      int w = proto.getIconWidth();
      int h = proto.getIconHeight();
      return new Dimension(20 + w + i.left + i.right, h + i.top + i.bottom);
    }

    @Override public void updateUI() {
      setRenderer(null);
      super.updateUI();
      setMaximumRowCount(3);
      setPrototypeDisplayValue(proto);
      ListCellRenderer<? super Icon> r = getRenderer();
      setRenderer((list, value, index, isSelected, cellHasFocus) -> {
        Component c = r.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
        if (c instanceof JLabel) {
          JLabel l = (JLabel) c;
          l.setIcon(value);
          l.setBorder(BorderFactory.createEmptyBorder());
        }
        return c;
      });

      ComboPopup popup = (ComboPopup) getAccessibleContext().getAccessibleChild(0);
      JList<?> list = popup.getList();
      list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
      list.setVisibleRowCount(3);
      list.setFixedCellWidth(proto.getIconWidth());
      list.setFixedCellHeight(proto.getIconHeight());
    }
  };
  combo.addPopupMenuListener(new PopupMenuListener() {
    private boolean adjusting;

    @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
      JComboBox<?> combo = (JComboBox<?>) e.getSource();

      Insets i = combo.getInsets();
      int popupWidth = proto.getIconWidth() * 3 + i.left + i.right;

      Dimension size = combo.getSize();
      if (size.width >= popupWidth) {
        return;
      }
      if (!adjusting) {
        adjusting = true;
        combo.setSize(popupWidth, size.height);
        combo.showPopup();
      }
      combo.setSize(size);
      adjusting = false;
    }

    @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
      /* not needed */
    }

    @Override public void popupMenuCanceled(PopupMenuEvent e) {
      /* not needed */
    }
  });
  return combo;
}

参考リンク

コメント