概要

JTableHeaderの各TableColumn間にロールオーバー時のみ出現するSeparatorJLayerを使用して描画します。

サンプルコード

class TableHeaderRolloverLayerUI extends LayerUI<JScrollPane> {
  private boolean rollover;

  @Override public void paint(Graphics g, JComponent c) {
    super.paint(g, c);
    if (c instanceof JLayer && rollover) {
      JScrollPane scroll = (JScrollPane) ((JLayer<?>) c).getView();
      JTable table = (JTable) scroll.getViewport().getView();
      JTableHeader header = table.getTableHeader();
      TableColumnModel cm = header.getColumnModel();
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setPaint(Color.GRAY);
      Line2D s = new Line2D.Double();
      int size = cm.getColumnCount();
      double gap = 2d;
      for (int i = 0; i < size; i++) {
        Rectangle r = header.getHeaderRect(i);
        double y1 = r.getY() + gap;
        double y2 = r.getY() + r.getHeight() - gap - gap;
        s.setLine(r.getX(), y1, r.getX(), y2);
        if (i != 0) {
          g2.draw(s);
        }
        if (i < size - 1) {
          double xx = r.getX() + r.getWidth() - gap;
          s.setLine(xx, y1, xx, y2);
          g2.draw(s);
        }
      }
      g2.dispose();
    }
  }

  @Override public void installUI(JComponent c) {
    super.installUI(c);
    if (c instanceof JLayer) {
      ((JLayer<?>) c).setLayerEventMask(
          AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
    }
  }

  @Override public void uninstallUI(JComponent c) {
    if (c instanceof JLayer) {
      ((JLayer<?>) c).setLayerEventMask(0);
    }
    super.uninstallUI(c);
  }

  @Override protected void processMouseEvent(
      MouseEvent e, JLayer<? extends JScrollPane> l) {
    super.processMouseEvent(e, l);
    Component c = e.getComponent();
    if (e.getID() == MouseEvent.MOUSE_RELEASED) {
      rollover = c.getBounds().contains(e.getPoint());
      c.repaint();
    }
  }

  @Override protected void processMouseMotionEvent(
      MouseEvent e, JLayer<? extends JScrollPane> l) {
    super.processMouseMotionEvent(e, l);
    Component c = e.getComponent();
    int id = e.getID();
    boolean b = id == MouseEvent.MOUSE_MOVED || id == MouseEvent.MOUSE_DRAGGED;
    rollover = b && c instanceof JTableHeader;
    l.repaint(c.getBounds());
  }
}
View in GitHub: Java, Kotlin

解説

  • JTableHeader#setDefaultRenderer(...)LookAndFeelデフォルトのBorderやフォーカス描画を除去したフラットなセルレンダラーを設定
    • JTable#setShowGrid(false)などはJTableHeaderのグリッド表示などには影響しない
  • JTableを配置したJScrollPaneJLayerをラップして現在ロールオーバー状態のTableColumnだけではなくすべてのTableColumn間にSeparatorを描画する
    • LayerUI#installUI()AWTEvent.MOUSE_EVENT_MASKAWTEvent.MOUSE_MOTION_EVENT_MASKを設定してマウスイベントをJLayerで取得するよう設定
    • LayerUI#processMouseMotionEvent(...)などでJTableHeader上にマウスカーソルが乗ったらロールオーバー状態をtrueに設定してJTableHeaderをリペイント
    • LayerUI#paint()をオーバーライドしてJTableHeaderがロールオーバー状態の場合はJTableHeader#getHeaderRect(int)で取得した各領域の左右辺にLine2DSeparatorを描画
    • TableColumnをリサイズ中にJTableHeader外でマウスリリースした場合はSeparatorを非表示に変更

参考リンク

コメント