• category: swing folder: FocusBorder title: JTextFieldにフォーカスの有無でフチ色を変更するBorderを設定する tags: [JTextField, Focus, Border, LookAndFeel] author: aterai pubdate: 2023-10-30T00:32:37+09:00 description: JTextFieldにフォーカスが適用されたらそのフチ色を変更してハイライトするBorderを設定します。 image: https://drive.google.com/uc?id=1uxBRQ_jk5WrG6Q6E7Jqg0BaaVQFLgrYD

概要

JTextFieldにフォーカスが適用されたらそのフチ色を変更してハイライトするBorderを設定します。

サンプルコード

JTextField field2 = new JTextField(20) {
  private transient FocusListener handler;

  @Override public void updateUI() {
    removeFocusListener(handler);
    super.updateUI();
    setOpaque(false);
    setBorder(new RoundedCornerBorder());
    handler = new FocusBorderListener();
    addFocusListener(handler);
  }

  @Override protected void paintComponent(Graphics g) {
    Border b = getBorder();
    if (!isOpaque() && b instanceof RoundedCornerBorder) {
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setPaint(getBackground());
      int w = getWidth() - 1;
      int h = getHeight() - 1;
      g2.fill(((RoundedCornerBorder) b).getBorderShape(0, 0, w, h));
      g2.dispose();
    }
    super.paintComponent(g);
  }
};

// ...

class RoundedCornerBorder extends AbstractBorder {
  private static final Paint ALPHA_ZERO = new Color(0x0, true);
  private static final int ARC = 4;

  @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    Shape border = getBorderShape(x, y, width - 1, height - 1);

    g2.setPaint(ALPHA_ZERO);
    Area corner = new Area(new Rectangle2D.Double(x, y, width, height));
    corner.subtract(new Area(border));
    g2.fill(corner);

    Color borderColor;
    if (c.hasFocus()) {
      borderColor = new Color(0x4F_C1_E9);
    } else if (c.isEnabled()) {
      borderColor = Color.LIGHT_GRAY;
    } else {
      borderColor = Color.WHITE;
    }
    g2.setPaint(borderColor);
    g2.draw(border);
    g2.dispose();
  }

  public Shape getBorderShape(int x, int y, int w, int h) {
    return new RoundRectangle2D.Double(x, y, w, h, ARC, ARC);
  }

  @Override public Insets getBorderInsets(Component c) {
    return new Insets(ARC, ARC, ARC, ARC);
  }

  @Override public Insets getBorderInsets(Component c, Insets insets) {
    insets.set(ARC, ARC, ARC, ARC);
    return insets;
  }
}

class FocusBorderListener implements FocusListener {
  @Override public void focusGained(FocusEvent e) {
    update(e.getComponent());
  }

  @Override public void focusLost(FocusEvent e) {
    update(e.getComponent());
  }

  private void update(Component c) {
    c.repaint();
  }
}
View in GitHub: Java, Kotlin

解説

  • JTextFieldに角丸のBorderを設定
  • このFocusBorderpaintBorder(...)メソッドをオーバーライドし、Component#hasFocus()の場合はフチ色を変更するよう設定
  • デフォルトではフォーカスの有無でフチ色が変化しないMetalLookAndFeelWindowsLookAndFeelの場合、JTextFieldにフォーカスが設定されたらフチを含むコンポーネント全体を再描画するFocusListenerを追加する必要がある
    • NimbusLookAndFeelFocusListenerを実装するSynthTextFieldUI.Handlerで再描画を実行している
    • MotifLookAndFeelFocusListenerではなくDefaultCaret#focusGained(...)などをオーバーライドして再描画を実行している
public class MotifTextUI {
public static class MotifCaret extends DefaultCaret implements UIResource {
  @Override public void focusGained(FocusEvent e) {
    super.focusGained(e);
    getComponent().repaint();
  }

  @Override public void focusLost(FocusEvent e) {
    super.focusLost(e);
    getComponent().repaint();
  }
// ...

参考リンク

コメント