TITLE:JTableのセルの高さを自動調整
Posted by terai at 2010-10-25

JTableのセルの高さを自動調整

JTableのセルの高さを、文字列の折り返しに応じて自動調整します。
  • category: swing folder: AutoWrapTableCell title: JTableのセルの高さを自動調整 tags: [JTable, JTextArea, TableCellRenderer] author: aterai pubdate: 2010-10-25T14:24:03+09:00 description: JTableのセルの高さを、文字列の折り返しに応じて自動調整します。 image: https://lh3.googleusercontent.com/_9Z4BYR88imo/TQTH4TWFB1I/AAAAAAAAAR4/8C89wEJ8EUA/s800/AutoWrapTableCell.png hreflang:
       href: https://java-swing-tips.blogspot.com/2017/06/automatically-adjust-height-of-jtables.html
       lang: en

概要

JTableのセルの高さを、文字列の折り返しに応じて自動調整します。

#screenshot

サンプルコード

#spanend
#spanadd
class TextAreaCellRenderer extends JTextArea implements TableCellRenderer {
#spanend
  private final List<List<Integer>> cellHeights = new ArrayList<>();

#spandel
**サンプルコード [#f1f7d94f]
#spanend
#spandel
#code{{
#spanend
#spandel
JTable table = new JTable(model) {
#spanend
  @Override public void doLayout() {
    initPreferredHeight();
    super.doLayout();
  @Override public void updateUI() {
    super.updateUI();
    setLineWrap(true);
    setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
    setName("Table.cellRenderer");
  }
  @Override public void columnMarginChanged(ChangeEvent e) {
    initPreferredHeight();
    super.columnMarginChanged(e);
  @Override public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected, boolean hasFocus,
      int row, int column) {
    setFont(table.getFont());
    setText(Objects.toString(value, ""));
    adjustRowHeight(table, row, column);
    return this;
  }
  private void initPreferredHeight() {
    int vc = convertColumnIndexToView(AUTOWRAP_COLUMN);
    TableColumn col = getColumnModel().getColumn(vc);
    for(int row=0; row<getRowCount(); row++) {
      Component c = prepareRenderer(col.getCellRenderer(), row, vc);
      if(c instanceof JTextArea) {
        JTextArea a = (JTextArea)c;
        int h = getPreferredHeight(a); // + getIntercellSpacing().height;
        //if(getRowHeight(row)!=h)
        setRowHeight(row, h);
      }
#spanadd

#spanend
  /**
   * Calculate the new preferred height for a given row,
   * and sets the height on the table.
   * http://blog.botunge.dk/post/2009/10/09/JTable-multiline-cell-renderer.aspx
   */
  private void adjustRowHeight(JTable table, int row, int column) {
  // The trick to get this to work properly is to set the width of the column to
  // the textarea. The reason for this is that getPreferredSize(), without a width
  // tries to place all the text in one line.
  // By setting the size with the width of the column,
  // getPreferredSize() returnes the proper height which the row should have in
  // order to make room for the text.
  // int cWidth = table.getTableHeader().getColumnModel().getColumn(column).getWidth();
  // int cWidth = table.getCellRect(row, column, false).width; // IntercellSpacingを無視
  // setSize(new Dimension(cWidth, 1000));
#spanadd

#spanend
    setBounds(table.getCellRect(row, column, false)); // setSizeではなくsetBoundsでも可
    // doLayout(); // 必要なさそう
#spanadd

#spanend
    int preferredHeight = getPreferredSize().height;
    while (cellHeights.size() <= row) {
      cellHeights.add(new ArrayList<>(column));
    }
    List<Integer> list = cellHeights.get(row);
    while (list.size() <= column) {
      list.add(0);
    }
    list.set(column, preferredHeight);
    int max = list.stream().max(Integer::compare).get();
    if (table.getRowHeight(row) != max) {
      table.setRowHeight(row, max);
    }
  }
  //http://tips4java.wordpress.com/2008/10/26/text-utilities/
  private int getPreferredHeight(JTextComponent c) {
    Insets insets = c.getInsets();
    View view = c.getUI().getRootView(c).getView(0);
    int preferredHeight = (int)view.getPreferredSpan(View.Y_AXIS);
    return preferredHeight + insets.top + insets.bottom;
  }
#spandel
};
#spanend
#spanadd
}
#spanend
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、セルレンダラーに、setLineWrap(true)としたJTextAreaを使用し、カラムサイズの変更などがあったときに、そのJTextAreaの高さを取得してJTable#setRowHeight(int)メソッドで各行の高さとして設定しています。

解説

上記のサンプルでは、セルレンダラーにsetLineWrap(true)を指定したJTextAreaを使用し、カラムサイズの変更が実行されるたびにそのJTextAreaの高さを取得してJTable#setRowHeight(int)メソッドで各行の高さを更新しています。

参考リンク

  • Text Utilities « Java Tips Weblog
  • JTableのセル幅で文字列を折り返し
    • -
  • 上記のTextAreaCellRendererを複数インスタンス生成して一つのJTableに設定すると正常に折り返しできなくなる
    • セルの高さをキャッシュしているcellHeightsstaticにすれば解決するが、その場合複数のJTableに適用すると正常に動作しない
      #spanend
      #spanadd
      // NG
      #spanend
      #spanadd
      table.getColumnModel().getColumn(0).setCellRenderer(new TextAreaCellRenderer());
      #spanend
      #spanadd
      table.getColumnModel().getColumn(1).setCellRenderer(new TextAreaCellRenderer());
      #spanend
      
      #spandel
      **コメント [#y52d6f46]
      #spanend
      - 高さが微妙に更新されない場合がある…。 -- [[terai]] &new{2010-10-27 (水) 14:02:02};
      - フレームのサイズ(JTableの高さ)を微妙に調整すると、スクロールバーが表示・非表示を繰り返す場合がある。 -- [[terai]] &new{2010-11-02 (火) 20:23:55};
      - 上二つの原因は同じだと思うけど、今のところ何が問題なのか分からず、お手上げ状態です。 -- [[terai]] &new{2010-11-02 (火) 20:26:21};
      #spanadd
      // OK
      #spanend
      #spanadd
      TableCellRenderer renderer = new TextAreaCellRenderer();
      #spanend
      #spanadd
      table.getColumnModel().getColumn(0).setCellRenderer(renderer);
      #spanend
      #spanadd
      table.getColumnModel().getColumn(1).setCellRenderer(renderer);
      #spanend
      #spanadd
      

参考リンク

コメント