• category: swing folder: PlaqueBorder title: Path2Dで額縁風の図形を作成しBorderとして使用する tags: [Border, Path2D, EmptyBorder, JTextField] author: aterai pubdate: 2023-12-25T00:59:23+09:00 description: Path2Dのベジェ曲線を使用して角を内側に丸めた額縁風の図形を描画するBorderを作成し、JTextFieldなどのコンポーネントに設定します。 image: https://drive.google.com/uc?id=1jyF7GgJ28kyMLA1wtG9pR6LotgncjhKs

概要

Path2Dのベジェ曲線を使用して角を内側に丸めた額縁風の図形を描画するBorderを作成し、JTextFieldなどのコンポーネントに設定します。

サンプルコード

JScrollPane scroll = new JScrollPane(new JTable(8, 5)) {
  private static final int ARC = 8;

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

  @Override public void updateUI() {
    super.updateUI();
    setOpaque(false);
    setBackground(Color.WHITE);
    getViewport().setBackground(getBackground());
    setBorder(new PlaqueBorder(ARC) {
      @Override protected Shape getBorderShape(
            double x, double y, double w, double h, double r) {
        double rr = r * .5522;
        Path2D path = new Path2D.Double();
        path.moveTo(x, y + r);
        path.curveTo(
            x + rr, y + r, x + r, y + rr, x + r, y);
        path.lineTo(x + w - r, y);
        path.curveTo(
            x + w - r, y + rr, x + w - rr, y + r, x + w, y + r);
        path.lineTo(x + w, y + h - r);
        path.curveTo(
            x + w - rr, y + h - r, x + w - r, y + h - rr, x + w - r, y + h);
        path.lineTo(x + r, y + h);
        path.curveTo(
            x + r, y + h - rr, x + rr, y + h - r, x, y + h - r);
        // path.lineTo(x, y + r);
        path.closePath();
        return path;
      }
    });
  }
};
View in GitHub: Java, Kotlin

解説

  • Path2D#curveTo(...)
    • EmptyBorderを継承するPlaqueBorderを作成
    • PlaqueBorder#paintBorder(...)をオーバーライドしてその余白にPath2D#curveTo(...)Path2D#lineTo(...)で作成した額縁風の図形を描画
      • 余白内に線を描画するので内部のコンポーネントとは交差しない
    • 角のラウンド半径はPlaqueBorder#getBorderInsets().topの値を使用
    • Path2D#curveTo(...)で使用する制御点は頂点から半径の0.55228…倍(4d * (Math.sqrt(2d) - 1d) / 3d)の位置を使用
  • Area#subtract(...)
    • Rectangle2Dで作成した矩形の4隅をEllipse2Dで切り取って額縁風の図形を作成
    • JTextField#paintComponent(...)をオーバーライドしてPlaqueBorderの図形内のみ背景を描画し、角丸の外側部分はJTextFieldの角を丸める同様透明化
class PlaqueBorder extends EmptyBorder {
  protected PlaqueBorder(int arc) {
    super(arc, arc, arc, arc);
  }

  @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);
    g2.setPaint(Color.GRAY);
    Insets i = getBorderInsets(c);
    int arc = Math.min(i.top, Math.min(width / 2, height / 2));
    g2.draw(getBorderShape(x, y, width - 1d, height - 1d, arc));
    g2.dispose();
  }

  protected Shape getBorderShape(
        double x, double y, double w, double h, double r) {
    Area rect = new Area(new Rectangle2D.Double(x, y, w, h));
    rect.subtract(new Area(new Ellipse2D.Double(
        x - r, y - r, r + r, r + r)));
    rect.subtract(new Area(new Ellipse2D.Double(
        x + w - r, y - r, r + r, r + r)));
    rect.subtract(new Area(new Ellipse2D.Double(
        x - r, y + h - r, r + r, r + r)));
    rect.subtract(new Area(new Ellipse2D.Double(
        x + w - r, y + h - r, r + r, r + r)));
    return rect;
  }
}

参考リンク

コメント