如何在JTable中动态设置RowHeight

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

 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初学者,这是我的第一个问题。 我很高兴得到答案。

使用JTextArea作为渲染组件时存在几个问题(并且大多数(如果不是全部)已经在此站点上的几个QA中进行了解释)。 试着总结一下:

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

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

  • 使用数据配置其渲染器
  • 询问渲染组件的首选大小
  • 将表行高度设置为pref高度

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是错误的

许多好的想法与其他答案以及相关问题相关联。

所以这里是我如何修改你的类来获得一个完全工作的自动行高的表:

 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可见后才计算高度,因为在此之前我们可能没有正确的字体
  • 仅从event-dispatch-thread更改rowHeightSwingUtilities.isEventDispatchThread() / SwingUtilities.invokeLater() ); 否则,当我们处于计算高度的中间时,表可能会为我们的RowHeightCellRenderer分配不同的值
  • rowHeight为20,这样我们就可以再次收缩
  • TableColumnModelListener因此我们知道用户何时调整列的大小并缩小或增大rowHeight (如果不希望这样,您可以安全地删除监听器)

此外,现在我只评估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 

变化:

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

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

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

  1. 将自定义TableCellRenderer添加到返回TableCellRenderer中的JTextArea的表中#getTableCellRendererComponent()
  2. 按照此处的说明监听列的大小调整: Java JTable检测列由用户重新resize
  3. 更新列的行高,如下所述: 自动调整JTable中行的高度
  4. 在更新行高度期间,首先通过设置新宽度然后获取首选高度来计算JTextArea所需的大小,如下面的代码片段所示

更新行高的function:

 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检测列中的一些代码,用户重新 调整了 大小,并自动调整JTable中行的高度 。

通常我们RowHeight的文本超出rowheight时设置RowHeight ,以设置我们执行此操作的行的高度:

 table.setRowHeight(40); 

我想在一个比给定单元格宽度更长的JTable中放置一个String。 如何动态设置rowHeight以便我可以读取整个String

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

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

而且你将能够阅读整个字符串。


产量

产量

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

 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的渲染器。