TITLE:JTableの行ヘッダに列ヘッダのRendererを使用する

Posted by aterai at 2012-11-05

JTableの行ヘッダに列ヘッダのRendererを使用する

JTableの行の描画に、JTableHeaderから取得した列ヘッダのRendererを適用します。

  • &jnlp;
  • &jar;
  • &zip;
RowHeaderStyle.png

サンプルコード

class RowHeaderRenderer extends JLabel implements TableCellRenderer {
  public RowHeaderRenderer(JTable table) {
    super();
    RollOverListener rol = new RollOverListener();
    table.addMouseListener(rol);
    table.addMouseMotionListener(rol);
  }
  @Override public Component getTableCellRendererComponent(
      JTable tbl, Object val, boolean isS, boolean hasF, int row, int col) {
    TableCellRenderer tcr = tbl.getTableHeader().getDefaultRenderer();
    boolean f = row==rollOverRowIndex;
    JLabel l = (JLabel)tcr.getTableCellRendererComponent(
        tbl, val, isS, f?f:hasF, -1, -1);
    if(tcr.getClass().getName().indexOf("XPDefaultRenderer")>0) {
      l.setOpaque(!f);
      this.setIcon(new ComponentIcon(l));
      return this;
    } else {
      return l;
    }
  }
  private int rollOverRowIndex = -1;
  class RollOverListener extends MouseAdapter {
    @Override public void mouseMoved(MouseEvent e) {
      JTable table = (JTable)e.getSource();
      Point pt = e.getPoint();
      int col = table.columnAtPoint(pt);
      int column = table.convertColumnIndexToModel(col);
      if(column!=0) return;

      int prev_row = rollOverRowIndex;
      rollOverRowIndex = table.rowAtPoint(pt);
      if(rollOverRowIndex == prev_row) return;
      Rectangle repaintRect;
      if(rollOverRowIndex >= 0) {
        Rectangle r = table.getCellRect(rollOverRowIndex, col, false);
        if(prev_row >= 0) {
          repaintRect = r.union(table.getCellRect(prev_row, col, false));
        } else {
          repaintRect = r;
        }
      } else {
        repaintRect = table.getCellRect(prev_row, col, false);
      }
      table.repaint(repaintRect);
    }
    @Override public void mouseExited(MouseEvent e) {
      JTable table = (JTable)e.getSource();
      Point pt = e.getPoint();
      int col = table.columnAtPoint(pt);
      int column = table.convertColumnIndexToModel(col);
      if(column!=0) return;

      if(rollOverRowIndex >= 0) {
        table.repaint(table.getCellRect(rollOverRowIndex, col, false));
      }
      rollOverRowIndex = -1;
    }
  }
}
class ComponentIcon implements Icon {
  private final JComponent cmp;
  public ComponentIcon(JComponent cmp) {
    this.cmp = cmp;
  }
  @Override public int getIconWidth() {
    return 4000; //Short.MAX_VALUE;
  }
  @Override public int getIconHeight() {
    return cmp.getPreferredSize().height + 4; //XXX: +4 for Windows 7
  }
  @Override public void paintIcon(Component c, Graphics g, int x, int y) {
    SwingUtilities.paintComponent(
        g, cmp, (Container)c, x, y, getIconWidth(), getIconHeight());
  }
}
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、0列目のセルの描画に、JTable#getTableHeader()#getDefaultRenderer()で取得したレンダラーを使用するTableCellRendererを適用しています。

  • ソートされていても、ソートアイコンが描画されないようする
    • 引数の行と列を両方-1 にして、TableCellRenderer#getTableCellRendererComponent(...)で描画用コンポーネント(JLabel)をヘッダレンダラーから取得
  • Windows Look & Feel
    • ロールオーバーを描画する場合は、TableCellRenderer#getTableCellRendererComponent(...)で取得したコンポーネントを透明にする
    • 右と下側に余白が発生するので、ヘッダーレンダラーからサイズを変更したアイコンを作成して、JLabel#setIcon(Icon)で表示

参考リンク

コメント