【问题标题】:How to set the RowHeight dynamically in a JTable如何在 JTable 中动态设置 RowHeight
【发布时间】:2014-02-12 08:58:56
【问题描述】:

我想在JTable 中放置一个比给定单元格宽度更长的字符串。 如何动态设置rowHeight 以便读取整个字符串? 这是一个例子:

import javax.swing.*;

public class ExampleTable {

public JPanel createTable() {               
    JPanel totalGUI = new JPanel();

    //define titles for table
    String[] title = {"TITLE1", "TITLE2", "TITLE3"};

    //table data
    Object[][] playerdata = {       
    {new Integer(34), "Steve", "test test test"},
    {new Integer(32), "Patrick", "dumdi dumdi dummdi dumm di di didumm"},
    {new Integer(10), "Sarah", "blabla bla bla blabla bla bla blabla"},};

    //create object 'textTable'
    JTable textTable = new JTable(playerdata,title);

    //set column width
    textTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 
    textTable.getColumnModel().getColumn(0).setPreferredWidth(60);
    textTable.getColumnModel().getColumn(1).setPreferredWidth(60);
    textTable.setDefaultRenderer(String.class, new RowHeightCellRenderer());

    //scrollbar
    JScrollPane scrollPane = new JScrollPane(textTable);

    totalGUI.add(scrollPane);               
    return totalGUI;
}

private static void createAndShowGUI() {

    //create main frame
    JFrame mainFrame = new JFrame("");
    ExampleTable test = new ExampleTable();

    JPanel totalGUI = new JPanel();
    totalGUI = test.createTable();

    //visible mode
    mainFrame.add(totalGUI); //integrate main panel to main frame
    mainFrame.pack();
    mainFrame.setVisible(true);     
}


public static void main (String[] args) {               

    createAndShowGUI();     

}//main
}

在这里,您将看到对给定单元格较长的每个文本进行换行的代码

    import java.awt.*;
    import javax.swing.*;
    import javax.swing.table.*;


    public class RowHeightCellRenderer extends JTextArea implements TableCellRenderer
    {
      /**
         * 
         */
        private static final long serialVersionUID = 1L;

    public Component getTableCellRendererComponent (JTable table, 
                                                    Object value, 
                                                    boolean isSelected, 
                                                    boolean hasFocus, 
                                                    int row, 
                                                    int column )  {
        setText( value.toString() );    
        return this;
      }
    }

谢谢,但我想根据字符串长度动态实现 RowHeight... 我想阅读单元格中的整个字符串/文本。有什么建议吗?

我是 java 初学者,这是我的第一个问题。我很高兴能得到答案。

【问题讨论】:

  • 我看到了RowCellRender。我们能看到那堂课吗?我认为您可能走在正确的道路上。
  • 谢谢,我的代码只是一个例子,列宽是固定的。因此 Rowheight 必须自动调整自己
  • 注意:@peeskillet 的解决方案是在渲染器中设置表格的 rowHeight 时错误!相反,请听取所有可能需要调整的更改并在那里进行更改。
  • @kleopatra 我无法更正我对您标准的回答。请考虑回答这个问题以获得赏金(不是那些点对你来说很重要)广告我很想看到你的方法。或者对于其他想要接受这一挑战的人,请随意!
  • @user3300710 请取消接受此答案并等待更合适的答案接受。另外,如果赏金得到奖励,我也可以删除我的答案。

标签: java swing jtable


【解决方案1】:

在使用 JTextArea 作为渲染组件时有几个问题(如果不是全部的话,大多数问题已经在本网站的几个 QA 中进行了解释)。试图总结它们:

将单个行高调整为渲染组件的大小要求

基本上,要走的路是根据需要循环遍历单元格,然后

  • 用数据配置它的渲染器
  • 询问渲染组件的首选尺寸
  • 将表格行高设置为首选项高度

OP编辑问题中的updateRowHeight方法就可以了。

JTextArea 对其preferredSize 的计算

要获得一个维度的合理大小提示,需要在另一个维度上“播种”一些合理的大小。也就是说,如果我们想要它需要一个宽度的高度,并且必须在每次调用中完成。在表格的上下文中,合理的宽度是当前列宽:

public Component getTableCellRendererComponent(JTable table,
        Object value, boolean isSelected, boolean hasFocus, int row,
        int column) {
    ... // configure visuals
    setText((String) value);
    setSize(table.getColumnModel().getColumn(column).getWidth(),
            Short.MAX_VALUE);
    return this;
}// getTableCellRendererComponent

高度动态调整

它在表格/列/模型的某些稳定状态下完全确定的行高。因此,您在初始化完成后设置它(调用 updateRowHeight)一次,并且只要它所依赖的任何状态发生更改。

// TableModelListener
@Override
public void tableChanged(TableModelEvent e) {
    updateRowHeights();
}

// TableColumnModelListener
@Override
public void columnMarginChanged(ChangeEvent e) {
    updateRowHeights();
}

注意

作为一般规则,getXXRendererComponent 中的所有参数都是严格只读的,实现不得改变调用者的任何状态。从渲染器中更新 rowHeight 是错误的

【讨论】:

  • 有没有很好的方法:“所以你在初始化完成后设置它(调用updateRowHeight)一次”现在我正在使用一个componentListener,所以在第一次渲染表之后,表有一个地图,每个单元格的所有首选高度,然后我想调用 updateRowHeights 方法。我发现在渲染后调用它的唯一方法是在表上使用组件侦听器...
【解决方案2】:

许多好主意都与其他答案以及相关问题相关联。

以下是我如何修改您的类以获得具有完全工作的自动行高的表格:

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import java.awt.*;

public class ExampleTable implements TableModelListener {
    JTable textTable;

    public JPanel createTable() {
        JPanel totalGUI = new JPanel();

        //define titles for table
        String[] title = {"TITLE1", "TITLE2", "TITLE3"};

        //table data
        Object[][] playerdata = {
                {new Integer(3), "Steve", "test test test"},
                {new Integer(32), "Patrick", "du hu hu hu hu hu hu hu uh u kkkkkk oooo pppp"},
                {new Integer(10), "Sarah", "xxxxxxxxxxxxx aaaaaaaaaa bbbbbbbbbbbb dddddddddddd xxxxxxx gggewr  eeeeeeeeee22 ddd g fffffff zzzzzzz"},};

        //define tablemodel
        TableModel model = new DefaultTableModel(playerdata, title);

        //create object 'textTable'
        textTable = new JTable(model);

        //set column width
        textTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        textTable.getColumnModel().getColumn(0).setPreferredWidth(17);
        textTable.getColumnModel().getColumn(1).setPreferredWidth(45);
        textTable.getColumnModel().getColumn(2).setPreferredWidth(200);

        //put line breaks if string is longer than cell-width
        RowHeightCellRenderer dynRow = new RowHeightCellRenderer();
        textTable.getColumnModel().getColumn(2).setCellRenderer(dynRow);

        // No more data changes; install listeners
        textTable.getModel().addTableModelListener(this);
        textTable.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
            /**
             * We only need to recalculate once; so track if we are already going to do it.
             */
            boolean columnHeightWillBeCalculated = false;

            @Override
            public void columnAdded(TableColumnModelEvent e) {
            }

            @Override
            public void columnRemoved(TableColumnModelEvent e) {
            }

            @Override
            public void columnMoved(TableColumnModelEvent e) {
            }

            @Override
            public void columnMarginChanged(ChangeEvent e) {
                if (!columnHeightWillBeCalculated && textTable.getTableHeader().getResizingColumn() != null) {
                    columnHeightWillBeCalculated = true;
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            // textTable.getTableHeader().getResizingColumn() is != null as long as the user still is holding the mouse down
                            // To avoid going over all data every few milliseconds wait for user to release
                            if (textTable.getTableHeader().getResizingColumn() != null) {
                                SwingUtilities.invokeLater(this);
                            } else {
                                tableChanged(null);
                                columnHeightWillBeCalculated = false;
                            }
                        }
                    });
                }
            }

            @Override
            public void columnSelectionChanged(ListSelectionEvent e) {
            }
        });

        //scrollbar
        JScrollPane scrollPane = new JScrollPane(textTable);
        totalGUI.add(scrollPane);
        return totalGUI;
    }

    public void tableChanged(TableModelEvent e) {
        final int first;
        final int last;
        if (e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW) {
            // assume everything changed
            first = 0;
            last = textTable.getModel().getRowCount();
        } else {
            first = e.getFirstRow();
            last = e.getLastRow() + 1;
        }
        // GUI-Changes should be done through the EventDispatchThread which ensures all pending events were processed
        // Also this way nobody will change the text of our RowHeightCellRenderer because a cell is to be rendered
        if(SwingUtilities.isEventDispatchThread()) {
            updateRowHeights(first, last);
        } else {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    updateRowHeights(first, last);
                }
            });
        }
    }

    private void updateRowHeights(final int first, final int last) {
    /*
     * Auto adjust the height of rows in a JTable.
     * The only way to know the row height for sure is to render each cell
     * to determine the rendered height. After your table is populated with
     * data you can do:
     *
     */
        for (int row = first; row < last; row++) {
            int rowHeight = 20;
            for (int column = 0; column < textTable.getColumnCount(); column++) {
                Component comp = textTable.prepareRenderer(textTable.getCellRenderer(row, column), row, column);
                rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);
            }
            if(rowHeight != textTable.getRowHeight(row)) {
                textTable.setRowHeight(row, rowHeight);
                System.out.println("neue Zeilenhöhe: "+rowHeight+" bei Zeile: "+row);
            }
        }
    }

    private static void createAndShowGUI() {

        //create main frame
        JFrame mainFrame = new JFrame("");
        mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ExampleTable test = new ExampleTable();
        JPanel totalGUI = new JPanel();
        totalGUI = test.createTable();

        //visible mode
        mainFrame.add(totalGUI); //integrate main panel to main frame
        mainFrame.pack();
        mainFrame.setVisible(true);

        //adjust dynamically the Row height of each cell
        test.tableChanged(null);
    }


    public static void main (String[] args) {
        createAndShowGUI();
    }//main
}

重要变化:

  • 只有在JFrame 可见后才计算高度,因为在那之前我们可能没有正确的字体
  • 仅从事件调度线程(SwingUtilities.isEventDispatchThread()/SwingUtilities.invokeLater())更改rowHeight;否则表格可能会在我们计算高度的过程中为我们的RowHeightCellRenderer 分配不同的值
  • rowHeight初始化为20,这样我们可以再次收缩
  • TableColumnModelListener 所以我们知道用户何时调整列大小并缩小或增长rowHeights(如果不希望这样做,您可以安全地删除侦听器)

现在我只评估 TableModel 更改的更改行

import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.text.BadLocationException;

public class RowHeightCellRenderer extends JTextArea implements TableCellRenderer {

    private static final long serialVersionUID = 1L;


    public RowHeightCellRenderer() {
        setLineWrap(true);
        setWrapStyleWord(true);
    }//constructor

    public Component getTableCellRendererComponent (JTable table,
                                                    Object value,
                                                    boolean isSelected,
                                                    boolean hasFocus,
                                                    int row,
                                                    int column ) {
        this.setText((String) value);

        if(isSelected) {
            this.setBackground(table.getSelectionBackground());
            this.setForeground(table.getSelectionForeground());
        } else {
            this.setBackground(table.getBackground());
            this.setForeground(table.getForeground());
        }

        final int columnWidth = table.getColumnModel().getColumn(column).getWidth();
        final int rowHeight = table.getRowHeight(row);
        this.setSize(columnWidth, rowHeight);

        this.validate();
        return this;
    }//getTableCellRendererComponent

    @Override
    public Dimension getPreferredSize() {
        try {
            // Get Rectangle for position after last text-character
            final Rectangle rectangle = this.modelToView(getDocument().getLength());
            if(rectangle != null) {
                return new Dimension(this.getWidth(),
                                     this.getInsets().top + rectangle.y + rectangle.height +
                                                                  this.getInsets().bottom);
            }
        } catch (BadLocationException e) {
            e.printStackTrace();  // TODO: implement catch
        }

        return super.getPreferredSize();
    }
}//RowHeightCellRenderer

变化:

  • setSize() in getTableCellRendererComponent() 否则对象无法正确包装
  • 根据最后一个字符的位置计算preferedSize

【讨论】:

  • @constructor:您的重要更改对我来说是非常有用的提示。谢谢!
【解决方案3】:

实际上,JTextArea 已经实现了根据宽度动态改变高度所需的所有功能。当 JTextArea 在 ScrollPane 中使用时,可以看到此功能的实际效果,它的高度会自动调整以适应 ScrollPane 的宽度。要使用此功能,必须首先将 JTextArea 的大小设置为某个宽度,然后 JTextArea#getPreferredSize() 将返回显示文本所需的高度(如果换行设置为 true)。

因此,要根据 JTable 的宽度动态更改 JTable 的行高,可以执行以下操作:

  1. 在 TableCellRenderer#getTableCellRendererComponent() 中将自定义 TableCellRenderer 添加到返回 JTextArea 的表中
  2. 收听列的大小调整,如下所述:Java JTable detect Column re-sized by user
  3. 如下所述更新列的行高:Auto adjust the height of rows in a JTable
  4. 在行高更新期间,首先设置新的宽度,然后获取首选高度,计算 JTextArea 所需的大小,如下面的代码 sn-p 所示

行高更新函数:

public static void updateRowHeights(int column, int width, JTable table){
    for (int row = 0; row < table.getRowCount(); row++) {
        int rowHeight = table.getRowHeight();
        Component comp = table.prepareRenderer(table.getCellRenderer(row, column), row, column);
        Dimension d = comp.getPreferredSize();
            // first set the size to the new width
        comp.setSize(new Dimension(width, d.height));
            // then get the preferred size
        d = comp.getPreferredSize();
        rowHeight = Math.max(rowHeight, d.height);
            // finally set the height of the table
        table.setRowHeight(row, rowHeight);
    }
}

使用这种方法,不需要进一步计算列或行。

这是 OP 的 ExampleTable 的完整代码,已针对此实现进行了调整。

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

public class ExampleTable {

    public class RowHeightCellRenderer extends JTextArea implements TableCellRenderer {

        public Component getTableCellRendererComponent(
                JTable table, Object value,
                boolean isSelected, boolean hasFocus,
                int row, int column) {

            setEditable(false);
            setLineWrap(true);
            setWrapStyleWord(true);

            if (isSelected) {
                setBackground(table.getSelectionBackground());
                setForeground(table.getSelectionForeground());
            } else {
                setBackground(table.getBackground());
                setForeground(table.getForeground());
            }

            setText(value.toString());
            return this;
        }
    }

    public static void updateRowHeights(int column, int width, JTable table){
        for (int row = 0; row < table.getRowCount(); row++) {
            int rowHeight = table.getRowHeight();
            Component comp = table.prepareRenderer(table.getCellRenderer(row, column), row, column);
            Dimension d = comp.getPreferredSize();
            comp.setSize(new Dimension(width, d.height));
            d = comp.getPreferredSize();
            rowHeight = Math.max(rowHeight, d.height);
            table.setRowHeight(row, rowHeight);
        }
    }

    public JPanel createTable() {

        JPanel totalGUI = new JPanel();

        //define titles for table
        final String[] columnNames = {"TITLE1", "TITLE2", "TITLE3"};

        //table data
        final Object[][] rowData = {       
                {new Integer(34), "Steve", "test test test"},
                {new Integer(32), "Patrick", "dumdi dumdi dummdi dumm di di didumm"},
                {new Integer(10), "Sarah", "blabla bla bla blabla bla bla blabla"},};

        AbstractTableModel model = new AbstractTableModel() {
            @Override
            public Class<?> getColumnClass(int columnIndex) {
                return String.class;
            }

            public String getColumnName(int column) { return columnNames[column].toString(); }
            public int getRowCount() { return rowData.length; }
            public int getColumnCount() { return columnNames.length; }
            public Object getValueAt(int row, int col) { return rowData[row][col]; }
            public boolean isCellEditable(int row, int column) { return true; }
            public void setValueAt(Object value, int row, int col) {
                rowData[row][col] = value;
                fireTableCellUpdated(row, col);
            }
        };

        //create object 'textTable'
        final JTable textTable = new JTable();

        textTable.setDefaultRenderer(String.class, new RowHeightCellRenderer());
        textTable.setModel(model);

        //set column width
        textTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 
        textTable.getColumnModel().getColumn(0).setPreferredWidth(60);
        textTable.getColumnModel().getColumn(1).setPreferredWidth(60);

        ColumnListener cl = new ColumnListener(){

            @Override
            public void columnMoved(int oldLocation, int newLocation) {
            }

            @Override
            public void columnResized(int column, int newWidth) {
                updateRowHeights(column, newWidth, textTable);
            }

        };

        textTable.getColumnModel().addColumnModelListener(cl);
        textTable.getTableHeader().addMouseListener(cl);

        // initial update of row heights
        TableColumn c = textTable.getColumnModel().getColumn(2);
        updateRowHeights(2, c.getWidth(), textTable);

        //scrollbar
        JScrollPane scrollPane = new JScrollPane(textTable);

        totalGUI.add(scrollPane);               
        return totalGUI;
    }

    private static void createAndShowGUI() {

        //create main frame
        JFrame mainFrame = new JFrame("");
        mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ExampleTable test = new ExampleTable();

        JPanel totalGUI = new JPanel();
        totalGUI = test.createTable();

        //visible mode
        mainFrame.add(totalGUI); //integrate main panel to main frame
        mainFrame.pack();
        mainFrame.setVisible(true);     
    }


    public static void main (String[] args) {               

        createAndShowGUI();     

    }//main


    abstract class ColumnListener extends MouseAdapter implements TableColumnModelListener {

        private int oldIndex = -1;
        private int newIndex = -1;
        private boolean dragging = false;

        private boolean resizing = false;
        private int resizingColumn = -1;
        private int oldWidth = -1;

        @Override
        public void mousePressed(MouseEvent e) {
            // capture start of resize
            if(e.getSource() instanceof JTableHeader) {
                JTableHeader header = (JTableHeader)e.getSource();
                TableColumn tc = header.getResizingColumn();
                if(tc != null) {
                    resizing = true;
                    JTable table = header.getTable();
                    resizingColumn = table.convertColumnIndexToView( tc.getModelIndex());
                    oldWidth = tc.getPreferredWidth();
                } else {
                    resizingColumn = -1;
                    oldWidth = -1;
                }
            }   
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            // column moved
            if(dragging && oldIndex != newIndex) {
                columnMoved(oldIndex, newIndex);
            }
            dragging = false;
            oldIndex = -1;
            newIndex = -1;

            // column resized
            if(resizing) {
                if(e.getSource() instanceof JTableHeader) {
                    JTableHeader header = (JTableHeader)e.getSource();
                    TableColumn tc = header.getColumnModel().getColumn(resizingColumn);
                    if(tc != null) {
                        int newWidth = tc.getPreferredWidth();
                        if(newWidth != oldWidth) {
                            columnResized(resizingColumn, newWidth);
                        }
                    }
                }   
            }
            resizing = false;
            resizingColumn = -1;
            oldWidth = -1;
        }

        @Override
        public void columnAdded(TableColumnModelEvent e) {      
        }

        @Override
        public void columnRemoved(TableColumnModelEvent e) {        
        }

        @Override
        public void columnMoved(TableColumnModelEvent e) {
            // capture dragging
            dragging = true;
            if(oldIndex == -1){
                oldIndex = e.getFromIndex();
            }

            newIndex = e.getToIndex();  
        }

        @Override
        public void columnMarginChanged(ChangeEvent e) {
        }

        @Override
        public void columnSelectionChanged(ListSelectionEvent e) {
        }

        public abstract void columnMoved(int oldLocation, int newLocation);
        public abstract void columnResized(int column, int newWidth);
    }
}

请注意,我重复使用并更改了 Java JTable detect Column re-sized by userAuto adjust the height of rows in a JTable 中的一些代码。

【讨论】:

  • @peeskillet 是的,你当然是对的。我更改了 OP 的代码以重新创建渲染组件以进行测试,但忘记将其更改回来 - 感谢您的提醒。
  • 一点修正:在 updateRowHeights() 第 3 行应该是“int rowHeight = table.getRowHeight(row);”
【解决方案4】:

我发现从组件渲染器内部调整行高最简单,如下所示:

public class RowHeightCellRenderer extends JTextArea implements TableCellRenderer
{
    public Component getTableCellRendererComponent(JTable table, Object 
            value, boolean isSelected, boolean hasFocus,
            int row, int column) {

        setText(value.toString());

        // Set the component width to match the width of its table cell
        // and make the height arbitrarily large to accomodate all the contents
        setSize(table.getColumnModel().getColumn(column).getWidth(), Short.MAX_VALUE);

        // Now get the fitted height for the given width
        int rowHeight = this.getPreferredSize().height;

        // Get the current table row height
        int actualRowHeight = table.getRowHeight(row);

        // Set table row height to fitted height.
        // Important to check if this has been done already
        // to prevent a never-ending loop.
        if (rowHeight != actualRowHeight) {
           table.setRowHeight(row, rowHeight);
        }

        return this;
    }
}

这也适用于返回 JTextPane 的渲染器。

【讨论】:

    【解决方案5】:

    通常,当row 中的文本超出一行的height 时,我们会设置RowHeight,要设置一行的高度,我们这样做:

    table.setRowHeight(40);
    

    我想将一个字符串放入比给定单元格宽度长 的 JTable 中。如何动态设置 rowHeight 以便我可以读取整个字符串

    要从 jtable 行读取完整的字符串,您不需要设置 RowHeight,您需要设置 columnPreferredWidth,因为您已经这样做了。只需添加如下一行:

    textTable.getColumnModel().getColumn(2).setPreferredWidth(300);
    

    您将能够阅读整个字符串。


    输出

    【讨论】:

    • 谢谢,我的代码只是一个例子,列宽是固定的。因此 Rowheight 必须自动调整自己
    猜你喜欢
    • 1970-01-01
    • 2014-08-21
    • 1970-01-01
    • 2020-09-16
    • 1970-01-01
    • 2015-01-14
    • 1970-01-01
    • 2012-09-08
    • 2014-10-27
    相关资源
    最近更新 更多