概要

WindowsLookAndFeelJComboBoxの内部アイテムに適用される点線のFocusBorderを非表示にし、代替としてJComboBox自体に実線のFocusBorderを表示します。

サンプルコード

JComboBox<String> combo3 = new JComboBox<String>(model) {
  @Override public void updateUI() {
    setRenderer(null);
    super.updateUI();
    if (isWindowsLnF()) {
      setRenderer(new DefaultListCellRenderer() {
        @Override public Component getListCellRendererComponent(
            JList<?> list, Object value, int index,
            boolean isSelected, boolean cellHasFocus) {
          JLabel l = (JLabel) super.getListCellRendererComponent(
              list, value, index, isSelected, cellHasFocus);
          if (index < 0) {
            l.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
          }
          return l;
        }
      });
    }
  }

  @Override protected void paintBorder(Graphics g) {
    super.paintBorder(g);
    if (isFocusOwner() && !isPopupVisible() && isWindowsLnF()) {
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setPaint(Color.DARK_GRAY);
      g2.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
      g2.dispose();
    }
  }

  private boolean isWindowsLnF() {
    return getUI().getClass().getName().contains("WindowsComboBoxUI");
  }
};
View in GitHub: Java, Kotlin

解説

  • default
    • デフォルトのWindowsLookAndFeelで編集不可のJComboBoxにフォーカスがある場合、内部アイテム(セルレンダラー内部)に点線のFocusBorderが表示される
    • 点線と文字列の間隔が近く?若干うるさい感じがする
  • setFocusable(false)
    • JComboBox#setFocusable(false)で点線は非表示になるがフォーカスが当たらなくなる
  • setRenderer(...)
    • DefaultListCellRenderer#getListCellRendererComponent(...)メソッドをオーバーライドし、インデックスが0以下(JComboBox本体での内部アイテムの描画)の場合は空のBorderを使用することで点線を非表示に設定
    • WindowsLookAndFeel以外では、内部アイテムにFocusBorderは付かないのでこのセルレンダラーは適用しない
    • フォーカスが当たっているかどうかが判別しづらくなる
  • paintBorder(...)
    • 上記と同様のフォーカスがあっても点線を非表示にするセルレンダラーを適用
    • JComboBox#paintBorder(...)をオーバーライドして別途JComboBox本体にFocusBorderを描画

  • WindowsLookAndFeelでもUIManager.put("ComboBox.border", ...)JComboBox本体のBorderを変更することは可能だが、内部アイテムに適用されるBorderを変更する方法は現状では存在しない?
UIManager.put("ComboBox.border", new LineBorder(Color.GRAY.brighter()) {
  @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
    super.paintBorder(c, g, x, y, width, height);
    if (c.isFocusOwner()) {
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setPaint(Color.DARK_GRAY);
      g2.drawRect(x, y, width - 1, height - 1);
      g2.dispose();
    }
  }
});

参考リンク

コメント