• category: swing folder: TableCellBorderHoverEffects title: JTableのセルBorderをホバー効果でハイライトする tags: [JTable, MouseMotionListener, RadialGradientPaint, Calendar] author: aterai pubdate: 2025-05-12T02:47:51+09:00 description: JTableのセルにすき間を設定し、マウスポインタ周辺のすき間のみを描画することでセルの縁を強調表示します。 image: https://drive.google.com/uc?id=1nUJ8fMADXR9DEfEXEw2fa6GzqwRnAjfL

Summary

JTableのセルにすき間を設定し、マウスポインタ周辺のすき間のみを描画することでセルの縁を強調表示します。

Source Code Examples

private final class MonthTable extends JTable {
  private final Point pt = new Point(-1000, -1000);
  private transient MouseAdapter listener;

  @Override public void updateUI() {
    removeMouseListener(listener);
    removeMouseMotionListener(listener);
    super.updateUI();
    setFillsViewportHeight(true);
    setOpaque(false);
    setShowGrid(false);
    setIntercellSpacing(new Dimension(2, 2));
    setFont(getFont().deriveFont(Font.BOLD));
    setDefaultRenderer(LocalDate.class, new CalendarTableRenderer());
    JTableHeader header = getTableHeader();
    TableCellRenderer r = new CenterAlignmentHeaderRenderer();
    TableColumnModel cm = getColumnModel();
    EventQueue.invokeLater(() -> {
      for (int i = 0; i < cm.getColumnCount(); i++) {
        cm.getColumn(i).setHeaderRenderer(r);
      }
    });
    header.setResizingAllowed(false);
    header.setReorderingAllowed(false);
    listener = new SpotlightListener();
    addMouseListener(listener);
    addMouseMotionListener(listener);
  }

  @Override protected void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setComposite(AlphaComposite.Src);
    Point2D center = new Point2D.Float(pt.x, pt.y);
    float[] dist = {0.0f, 0.5f, 1.0f};
    Color[] colors = {Color.GRAY, Color.LIGHT_GRAY, Color.WHITE};
    Rectangle cr = getCellRect(0, 0, true);
    int r = Math.max(cr.width, cr.height) * 2;
    g2.setPaint(new RadialGradientPaint(center, r, dist, colors));
    int r2 = r + r;
    g2.fill(new Ellipse2D.Float(pt.x - r, pt.y - r, r2, r2));
    g2.dispose();
    super.paintComponent(g);
  }

  private final class SpotlightListener extends MouseAdapter {
    @Override public void mouseExited(MouseEvent e) {
      pt.setLocation(-1000, -1000);
      repaint();
    }

    @Override public void mouseEntered(MouseEvent e) {
      update(e);
    }

    @Override public void mouseDragged(MouseEvent e) {
      update(e);
    }

    @Override public void mouseMoved(MouseEvent e) {
      update(e);
    }

    private void update(MouseEvent e) {
      pt.setLocation(e.getPoint());
      Rectangle cr = getCellRect(0, 0, true);
      int r = Math.max(cr.width, cr.height) * 2;
      int r2 = r + r;
      repaint(new Rectangle(pt.x - r, pt.y - r, r2, r2));
    }
  }

  // ...
}
View in GitHub: Java, Kotlin

Explanation

  • JTable#setShowGrid(false)で水平・垂直グリッド線を非表示に設定
  • JTable#setIntercellSpacing(new Dimension(2, 2))で各セルに縦横2pxのすき間を設定
       - `JTable#setOpaque(false)`で`JTable`の背景を描画しないよう設定
  • JTableMouseListenerMouseMotionListenerを追加してマウスポインタの座標を取得可能に設定し、マウスポインタの座標が変化したらその周辺をJTable#repaint(Rectangle)で再描画
  • JTable#paintComponent(...)をオーバーライドしてマウスポインタの座標を中心としたEllipse2D図形を作成し、その内部をRadialGradientPaintで塗りつぶす
       - 上記の塗りつぶしの後で`super.paintComponent(g)`を実行して`JTable`のセルなどを描画するが、`JTable#setIntercellSpacing(new Dimension(2, 2))`で設定したセル間のすき間はなに上書きされないのでセル`Border`がホバー効果風にハイライト表示される

Reference

Comment