TITLE:JTableに行ヘッダを追加

Posted by terai at 2006-09-04

JTableに行ヘッダを追加

JTableに行ヘッダを追加を追加します。

  • &jnlp;
  • &jar;
  • &zip;

#screenshot

サンプルコード

class RowHeaderList extends JList {
  private final JTable table;
  private final ListSelectionModel tableSelection;
  private final ListSelectionModel rListSelection;
  public RowHeaderList(ListModel model, JTable table) {
    super(model);
    this.table = table;
    setFixedCellHeight(table.getRowHeight());
    setCellRenderer(new RowHeaderRenderer(table.getTableHeader()));
    RollOverListener rol = new RollOverListener();
    addMouseListener(rol);
    addMouseMotionListener(rol);
    tableSelection = table.getSelectionModel();
    rListSelection = getSelectionModel();
  }
  class RowHeaderRenderer extends JLabel implements ListCellRenderer {
    private final JTableHeader header;
    public RowHeaderRenderer(JTableHeader header) {
      this.header = header;
      this.setOpaque(true);
      //this.setBorder(UIManager.getBorder("TableHeader.cellBorder"));
      this.setBorder(BorderFactory.createMatteBorder(0,0,1,2,Color.GRAY));
      this.setHorizontalAlignment(CENTER);
      this.setForeground(header.getForeground());
      this.setBackground(header.getBackground());
      this.setFont(header.getFont());
    }
    public Component getListCellRendererComponent(JList list, Object value,
               int index, boolean isSelected, boolean cellHasFocus) {
      if(index==pressedRowIndex) {
        setBackground(Color.GRAY);
      }else if(index==rollOverRowIndex) {
        setBackground(Color.WHITE);
      }else if(isSelected) {
        setBackground(Color.GRAY.brighter());
      }else{
        setForeground(header.getForeground());
        setBackground(header.getBackground());
      }
      setText((value==null)?"":value.toString());
      return this;
    }
  }
}

解説

上記のサンプルでは、JListで作成した行ヘッダをJScrollPaneにsetRowHeaderViewメソッドで追加しています。

表の余白などに色がついているのは、テストの名残です。特に意味は無いのですが、そのまま残しています。

rowHeader.setBackground(Color.BLUE);
scrollPane.setBackground(Color.RED);
scrollPane.getViewport().setBackground(Color.GREEN);

参考リンク

コメント

  • 不正なセルレンダラーを設定していたので修正しました。 -- terai
  • excelのように、行ヘッダをクリックしてその行が選択されたり、テーブルの本体にてセルをクリックしてそのセルだけが選択されたりすることはできますか?いろいろ試しましたが、なかなかできませんでした。 -- javalover
    • セル選択は、table.setCellSelectionEnabled(true);で可能です。行ヘッダをクリックしてその行を選択することは、現在でも出来るような。もし、列のことなら、JTableHeaderに以下のようなコードを書けばよさそうです。 JTableHeaderをクリックしてそのColumnのセルを全選択を参考にしてください。 -- terai
    • あ、もしかして、TableCellRendererでセルの背景色を変更 の例のように、JTable#prepareRendererをオーバーライドして行ごとの背景色を変更していますか? あちらの例では、行選択しか考慮していなのでisRowSelectedを使って、一行まるごと選択色で塗りつぶすかどうかを判断しています*1が、セル選択する場合は、ちゃんとisCellSelected(int,int)でそのセルが選択されているかを判断する必要があります。 -- terai
  • ここのサンプルを変更して、行列ヘッダクリックで、各行列を選択するように変更しました。 -- terai
  • プログラム自体大学で始めて触れてる者で参考にさせてもらっています。 -- D.Umeda(ES)
  • すいません、上のミスです。改行と思ってEnterKeyを..。DefaultTableCellRenderer r1 = (DefaultTableCellRenderer)table.getTableHeader().getDefaultRenderer()をした上でこのr1を必要なColumにsetCellRendererしてやれば、その機能は再現できるのではないでしょうか?
  • Ver1.6環境Vistaで実行しましたが、オンマウス時の背景変化以外は全て実装できているように思います。
  • JScrollPaneに対してJPanelを追加、その上にテーブルヘッダを1行ずつ追加していけばVista仕様でもXP仕様でもオンマウスの変化を再現できますが、Vistaの場合、グラフィックの問題で1pxずれる上、結局リスナを設定しなければヘッダ選択による行の全選択が出来ないので前者の方が効率が良いですが。
  • 逐一ヘッダを作って張ってなので後者は処理速度的にも実装形式的にも非効率だと思われます。ちなみにDefaultでもTableCellRendererでもVista上では結果は同じでWindowsClassicが適応されているように見えます。
  • 多々間違いがあれば訂正か削除をお願いします。長文、ミス失礼しました。 -- D.Umeda(ES)
    • どうもです。多分以下のような提案だと思っているのですが、合ってます? 処理速度とかは、この程度のサンプルだと、どちらもあまり気にしなくてもいいと思います。 -- terai
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class HeaderRendererTest{
  public JComponent makeUI() {
    String[] columnNames = {"", "String", "Boolean"};
    Object[][] data = {
      {0, "AAA", true}, {1, "BBB", false},
    };
    DefaultTableModel model = new DefaultTableModel(data, columnNames) {
      public Class<?> getColumnClass(int column) {
        return getValueAt(0, column).getClass();
      }
    };
    JTable table = new JTable(model);
    table.setRowSelectionAllowed(true);
    table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);

    JTableHeader header = table.getTableHeader();
    header.setReorderingAllowed(false);

    TableCellRenderer hr = header.getDefaultRenderer();
    TableColumn col = table.getColumnModel().getColumn(0);
    col.setCellRenderer(new HeaderRenderer(table, hr));

    JPanel p = new JPanel(new BorderLayout());
    p.add(new JScrollPane(table));
    p.setPreferredSize(new Dimension(320, 160));
    return p;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      public void run() {
        createAndShowGUI();
      }
    });
  }
  public static void createAndShowGUI() {
    try{
      UIManager.setLookAndFeel(
        UIManager.getSystemLookAndFeelClassName());
    }catch(Exception e) {
      e.printStackTrace();
    }
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.getContentPane().add(new HeaderRendererTest().makeUI());
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}
class HeaderRenderer implements TableCellRenderer {
  private final TableCellRenderer tcr;
  public HeaderRenderer(JTable table, TableCellRenderer tcr) {
    this.tcr  = tcr;
    RollOverListener rol = new RollOverListener();
    table.addMouseListener(rol);
    table.addMouseMotionListener(rol);
  }
  public Component getTableCellRendererComponent(
    JTable tbl, Object val, boolean isS,
    boolean hasF, int row, int col) {
    JLabel l;
    boolean flg = (row==rollOverRowIndex);
    l = (JLabel)tcr.getTableCellRendererComponent(
      tbl, val, isS, flg?flg:hasF, row, col);
    l.setOpaque(!flg);
    return l;
  }
  private int rollOverRowIndex = -1;
  private class RollOverListener extends MouseInputAdapter {
    public void mouseExited(MouseEvent e) {
      rollOverRowIndex = -1;
      JTable table = (JTable)e.getSource();
      table.repaint();
    }
    public void mouseMoved(MouseEvent e) {
      JTable table = (JTable)e.getSource();
      Point pt = e.getPoint();
      int column = table.columnAtPoint(pt);
      rollOverRowIndex = (column==0)?table.rowAtPoint(pt):-1;
      table.repaint();
    }
    public void mouseDragged(MouseEvent e) {}
    public void mousePressed(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}
  }
}
  • すばやい返答ありがとうございます。わざわざサンプルまで。自分で定義するHeaderRendererは無くとも「col.setCellRenderer(hr);」である程度再現できますが、やはりオンマウスやクリックでの変化はUI観点から見て必要ですね。やはり行ヘッダの領域を見るとどうしてもVistaだとズレが生じてしまうのが気になりますが、結局OS依存しないプログラムを作ろうとするとMetalかカスタムUIですね。列ヘッダをVista,行ヘッダをクラシックにする誤差は生じないのですが。 -- D.Umeda
    • Vistaは持ってないので、なんとも言えないのですが、XPでもオレンジのハイライト?が下に付くのはあれですね。まじめにやるなら右につけたいところです。-- terai
  • また醜い状態で設定してしまったorz見苦しいようであれば是非とも削除を。参考になるかは定かではありませんが、javaForumで掲示したところ次のようなサンプルURLを頂きました。http://tips4java.wordpress.com/2008/11/18/row-number-table/
    • camickrさんとこのブログですね。どこのjavaForum かは知らないのですが、いいとこ突いてると思います。 -- terai
  • 終わりに、わざわざサンプルを提示してくださってありがとうございました。これからも活用させてもらいます。
    • 文章で説明するより、サンプルのほうが簡単なので(^^;。 -- terai
  • 追記になりますが、処理速度の件は私がこれをする前に1列1行だけのヘッダをrowHeaderViewのパネルにひたすら並べ続けた、ということを書きたかったんですが、きちんと用語を用いて説明する勉強もしなければいけないようです。データ数によってはヘッダを量産するので非常に非効率だと思ったのですが、勉強しておきます。 -- D.Umeda
    • あー、前者と後者が何を指しているのか誤解してたみたいです。 -- terai
  • こんばんは。ページ上部の「このページを編集する」で、だれでも適当に編集できます。パスワードは日付を更新せずに編集する場合に必要なだけです。とりあえず勝手に改行入れときましたm(_ _)m。 -- terai
  • コメントなしとかおわっとる -- T