如何在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更改
rowHeight
(SwingUtilities.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的行高,可以执行以下操作:
- 将自定义TableCellRenderer添加到返回TableCellRenderer中的JTextArea的表中#getTableCellRendererComponent()
- 按照此处的说明监听列的大小调整: Java JTable检测列由用户重新resize
- 更新列的行高,如下所述: 自动调整JTable中行的高度
- 在更新行高度期间,首先通过设置新宽度然后获取首选高度来计算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
的文本超出row
的height
时设置RowHeight
,以设置我们执行此操作的行的高度:
table.setRowHeight(40);
我想在一个比给定单元格宽度更长的JTable中放置一个String。 如何动态设置rowHeight以便我可以读取整个String ?
要从jtable行读取完整的字符串,您不需要设置RowHeight
您需要设置column
的PreferredWidth
,因为您已经这样做了。 只需添加如下行:
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
的渲染器。