I want to put a String in a JTable that is longer than the given cell-width.
How can I set the rowHeight dynamically so that I can read the whole S
Many good ideas are linked in other answers and also in the related questions.
So here is how I modified you classes to get a table with fully working auto-line-heights:
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 ffffdffffdffffdffffd xxxxxxx gggewr eeeeeeeeee22 ffffd 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
}
Important changes:
JFrame is visible because we may not have the correct font before thenrowHeight only from event-dispatch-thread (SwingUtilities.isEventDispatchThread()/SwingUtilities.invokeLater()); otherwise the table might assign different values to our RowHeightCellRenderer while we are in the middle of calculating heightrowHeight to 20, so we can shrink againTableColumnModelListener so we know when the user resizes a column and shrink or grow rowHeights (you can safely remove the Listener if this is undesired)Also now I only evaluate changed rows for TableModel changes
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
Changes:
setSize() in getTableCellRendererComponent() as otherwise the object is unable to wrap correctlypreferedSize according to position of last character