---
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
---
* 概要 [#summary]
`JTextField`にフォーカスが適用されたらそのフチ色を変更してハイライトする`Border`を設定します。

#download(https://drive.google.com/uc?id=1uxBRQ_jk5WrG6Q6E7Jqg0BaaVQFLgrYD)

* サンプルコード [#sourcecode]
#code(link){{
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();
  }
}
}}

* 解説 [#explanation]
- `JTextField`に角丸の`Border`を設定
-- [[JTextFieldの角を丸める>Swing/RoundedTextField]]
- この`FocusBorder`の`paintBorder(...)`メソッドをオーバーライドし、`Component#hasFocus()`の場合はフチ色を変更するよう設定
- デフォルトではフォーカスの有無でフチ色が変化しない`MetalLookAndFeel`や`WindowsLookAndFeel`の場合、`JTextField`にフォーカスが設定されたらフチを含むコンポーネント全体を再描画する`FocusListener`を追加する必要がある
-- `NimbusLookAndFeel`は`FocusListener`を実装する`SynthTextFieldUI.Handler`で再描画を実行している
-- `MotifLookAndFeel`は`FocusListener`ではなく`DefaultCaret#focusGained(...)`などをオーバーライドして再描画を実行している
-- `MotifLookAndFeel`は`FocusListener`ではなく以下のように`DefaultCaret#focusGained(...)`などをオーバーライドして再描画を実行している

#code{{
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();
    }
// ...
}}

* 参考リンク [#reference]
- [[Borderのアニメーション>Swing/RippleBorder]]
- [[JComboBoxのFocusBorderの対象を内部のアイテムではなくJComboBox自体に変更する>Swing/ComboBoxFocusBorder]]
- [[JTextFieldにフォーカスがある場合の背景色を設定>Swing/FocusColor]]
- [[JTextFieldの角を丸める>Swing/RoundedTextField]]

* コメント [#comment]
#comment
#comment