---
category: swing
folder: RoundedCornerTableRowSelection
title: JTableの行選択背景描画をラウンド矩形に変更する
tags: [JTable, TableCellRenderer]
author: aterai
pubdate: 2024-04-08T04:32:15+09:00
description: JTableの行選択背景が行全体でラウンド矩形になるよう先頭・末尾セルのTableCellRendererで角を丸めて描画します。
image: https://drive.google.com/uc?id=1qsuWnjpaJmbyjNO6RsU9kv_JSIUpxTGY
---
* 概要 [#summary]
`JTable`の行選択背景が行全体でラウンド矩形になるよう先頭・末尾セルの`TableCellRenderer`で角を丸めて描画します。

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

* サンプルコード [#sourcecode]
#code(link){{
class RoundSelectionRenderer extends DefaultTableCellRenderer {
  private static final double ARC = 6d;
  private Position pos;

  @Override public void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(
        RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setColor(getBackground().brighter());
    double w = getWidth() - 1d;
    double h = getHeight() - 1d;
    Area area = pos.getArea(w, h, ARC);
    g2.fill(area);
    if (isFocusable()) {
      g2.setColor(getBackground());
      g2.draw(area);
    }
    super.paintComponent(g);
    g2.dispose();
  }

  @Override public Component getTableCellRendererComponent(
      JTable table, Object value,
      boolean isSelected, boolean hasFocus,
      int row, int column) {
    Component c = super.getTableCellRendererComponent(
        table, value, isSelected, false, row, column);
    if (c instanceof JLabel) {
      JLabel l = (JLabel) c;
      l.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
      l.setOpaque(false);
      pos = getPosition(table, column);
      boolean b = value instanceof Number;
      l.setHorizontalAlignment(b ? RIGHT : LEFT);
    }
    c.setFocusable(hasFocus
        || table.getSelectionModel().getLeadSelectionIndex() == row);
    return c;
  }

  private static Position getPosition(JTable table, int column) {
    boolean isFirst = column == 0;
    boolean isLast = column == table.getColumnCount() - 1;
    Position p;
    if (isFirst) {
      p = Position.FIRST;
    } else if (isLast) {
      p = Position.LAST;
    } else {
      p = Position.MIDDLE;
    }
    return p;
  }

  private enum Position {
    FIRST, MIDDLE, LAST;

    public Area getArea(double w, double h, double arc) {
      Area area = new Area();
      if (this == FIRST) {
        area.add(new Area(new Rectangle2D.Double(w - arc, 0d, arc + arc, h)));
        area.add(new Area(new RoundRectangle2D.Double(0d, 0d, w, h, arc, arc)));
      } else if (this == LAST) {
        area.add(new Area(new Rectangle2D.Double(-arc, 0d, arc + arc, h)));
        area.add(new Area(new RoundRectangle2D.Double(0d, 0d, w, h, arc, arc)));
      } else {
        area.add(new Area(new Rectangle2D.Double(-arc, 0d, w + arc + arc, h)));
      }
      return area;
    }
  }
}
}}

* 解説 [#explanation]
- `JTable#setShowGrid(false)`で罫線を非表示、`JTable#setRowHeight(24)`で行の高さを拡大、`JTable#setIntercellSpacing(new Dimension(0, 3))`で列の隙間を`0`に縮小、行の隙間を`3`に拡大して行選択描画が重ならないよう設定
- `JTable#setShowGrid(false)`で罫線を非表示、`JTable#setRowHeight(24)`で行の高さを拡大、`JTable#setIntercellSpacing(new Dimension(0, 3))`で列の隙間を`0`に縮小、行の隙間を`3`に拡大して行選択描画が重ならないよう設定(`Windows 11`のファイルエクスプローラー風)
- `DefaultTableCellRenderer#paintComponent(...)`をオーバーライドして先頭、末尾、それ以外のセルで選択背景の描画を変更
-- 先頭セル(`column == 0`)の場合は左端を`RoundRectangle2D`、右端をセル幅を右側方向にはみ出す`Rectangle2D`で作成した`Area`で選択背景を描画
--- フォーカス用の罫線も上記の`Area`を使用して描画
--- 右端の罫線はセル外で非表示になり、となりのセルと連続しているように表示される
-- 末尾セル(`column == table.getColumnCount() - 1`)の場合は右端を`RoundRectangle2D`、左端をセル幅を左側方向にはみ出す`Rectangle2D`で作成した`Area`で選択背景を描画
--- 先頭セル同様、左端の罫線はセル外で非表示
-- それ以外の中間セルの場合そのセル領域を左右にはみ出す`Rectangle2D`を作成して選択背景として描画
--- 左右の罫線はセル外で非表示

* 参考リンク [#reference]
- [[JTableHeaderの角を丸める>Swing/RoundedCornerTableHeader]]
-- 同様に先頭・末尾ヘッダセルの`TableCellRenderer`で角を丸めているが、ヘッダなのでフォーカス罫線は考慮していない

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