TITLE:JTableのセルを横方向に連結する
#navi(../)
#tags(JTable, TableCellRenderer, JTextArea, JScrollPane)
RIGHT:Posted by &author(aterai); at 2013-05-13
*JTableのセルを横方向に連結する [#y3d3b7a0]
``JTable``のセルを横方向に連結するセルレンダラーを作成します。

-&jnlp;
-&jar;
-&zip;

//#screenshot
#ref(https://lh5.googleusercontent.com/-wcXag_bBidU/UY-uA3riCRI/AAAAAAAABrs/Q_V-fdNVRu8/s800/ColumnSpanningCellRenderer.png)

**サンプルコード [#j27fd87a]
#code(link){{
class ColumnSpanningCellRenderer extends JPanel implements TableCellRenderer{
  private final JTextArea textArea = new JTextArea(1, 999999);
  private final JLabel label = new JLabel();
  private final JLabel iconLabel = new JLabel();
  public ColumnSpanningCellRenderer() {
    super(new BorderLayout(0,0));
    JScrollPane scroll = new JScrollPane(
      textArea, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
      ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    scroll.setBorder(BorderFactory.createEmptyBorder());
    scroll.setViewportBorder(BorderFactory.createEmptyBorder());
    textArea.setBorder(BorderFactory.createEmptyBorder());
    textArea.setMargin(new Insets(0,0,0,0));
    textArea.setForeground(Color.RED);
    textArea.setEditable(false);
    textArea.setFocusable(false);

    iconLabel.setBorder(BorderFactory.createEmptyBorder(0,4,0,4));
    iconLabel.setOpaque(false);

    Border b1 = BorderFactory.createEmptyBorder(2, 2, 2, 2);
    Border b2 = BorderFactory.createMatteBorder(0,0,1,1,Color.GRAY);
    label.setBorder(BorderFactory.createCompoundBorder(b2, b1));

    setBackground(textArea.getBackground());
    add(label, BorderLayout.NORTH);
    //add(iconLabel, BorderLayout.WEST);
    add(scroll);
  }
  @Override synchronized public Component getTableCellRendererComponent(
    JTable table, Object value, boolean isSelected,
    boolean hasFocus, int row, int column) {
    Test test;
    if(value instanceof Test) {
      test = (Test)value;
      add(iconLabel, BorderLayout.WEST);
    }else{
      int mrow = table.convertRowIndexToModel(row);
      String title = value!=null ? value.toString() : "";
      Test t = (Test)table.getModel().getValueAt(mrow, 0);
      String text = t!=null ? t.text : "";
      Icon icon = t!=null ? t.icon : null;
      test = new Test(title, icon, text);
      remove(iconLabel);
    }
    label.setText(test.title);
    textArea.setText(test.text);
    iconLabel.setIcon(test.icon);

    Rectangle cr = table.getCellRect(row, column, false);
    if(column!=0) {
      cr.x -= iconLabel.getPreferredSize().width;
    }
    textArea.scrollRectToVisible(cr);
    return this;
  }
}
}}

**解説 [#m5397014]
文字列を配置した``JTextArea``を各カラムごとに``JViewport``で表示する領域を切り取ってセルに貼り付けています。さらに、``JTable``のセルの縦罫線自体は、``table.setShowVerticalLines(false);``などで非表示にすることでレンダラー内の``JTextArea``は、連続しているように見せかけ、上部の``JLabel``は``Border``を設定することで区切りを表示しています。

- メモ
-- 列の入れ替えには対応していない
-- ``0``行目だけカラムヘッダのサイズを変更すると、描画がおかしくなる?
--- ``0``行目だけ高さ1のダミー行を追加して回避(ソートなどで問題が残る)
-- ``JTable``のクリック(セル選択?)などで表示が乱れる場合がある
--- ``JTable#repaint(Rectangle)``をオーバーライドして常に全体を描画することで回避

**参考リンク [#k0cadf9c]
- [http://docs.huihoo.com/javaone/2007/desktop/TS-3548.pdf PDF: Extreme GUI Makeover 2007]
-- via: [http://stackoverflow.com/questions/16305023/jtable-complex-cell-renderer java - JTable : Complex Cell Renderer - Stack Overflow]
- [[JTableの罫線の有無とセルの内余白を変更>Swing/IntercellSpacing]]

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