java - setmodel - Cómo configurar RowHeight dinámicamente en una JTable
setmodel java (5)
Quiero poner una cadena en una JTable
que sea más larga que el ancho de celda dado. ¿Cómo puedo configurar el rowHeight
dinámicamente para que pueda leer todo el String? Aquí hay un ejemplo:
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
}
Y aquí verá el código que rompe la línea de cada texto que es largo para la celda dada
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;
}
}
gracias, pero quiero implementar el RowHeight dinámicamente, dependiendo de la longitud de la cadena ... Quiero leer toda la cadena / texto en la celda. ¿alguna sugerencia?
Soy principiante de Java y esta es mi primera pregunta. Estaría encantado de recibir una respuesta.
Normalmente estamos configurando el RowHeight
cuando el texto en la row
va más allá de la height
de una fila, para establecer el alto de una fila hacemos esto:
table.setRowHeight(40);
Quiero poner una cadena en una tabla J que sea más larga que el ancho de celda dado . ¿Cómo puedo configurar el rowHeight dinámicamente para que pueda leer todo el String ?
Para leer una cadena completa de la fila jtable, no necesita configurar RowHeight
, necesita establecer el RowHeight
PreferredWidth
de una column
como ya lo está haciendo. Solo agrega una línea como:
textTable.getColumnModel().getColumn(2).setPreferredWidth(300);
Y podrás leer toda la cadena.
Salida
Hay varios problemas al usar un JTextArea como componente de representación (y la mayoría, si no todos, ya se explicaron en varios QA en este sitio). Intentando resumirlos:
Ajuste la altura de fila individual a los requisitos de tamaño del componente de representación
Básicamente, el camino a seguir es recorrer las celdas según sea necesario, luego
- configurar su procesador con los datos
- pregunte al componente de renderizado por su tamaño preferido
- establecer la altura de la fila de la tabla a la altura pref
El método updateRowHeight en la pregunta editada del OP está muy bien.
El cálculo de JTextArea de su preferredSize
para obtener una sugerencia de tamaño razonable para una dimensión, debe ser "sembrada" con un tamaño razonable en la otra dimensión. Eso es si queremos la altura que necesita un ancho, y eso debe hacerse en cada llamada. En el contexto de una tabla, un ancho razonable es el ancho de columna actual:
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
Ajuste dinámico de la altura
La altura de fila determina completamente en algún estado estable de la tabla / columna / modelo. Entonces lo configura (llama a updateRowHeight) una vez después de que se completa la inicialización y cuando cambia el estado del que depende.
// TableModelListener
@Override
public void tableChanged(TableModelEvent e) {
updateRowHeights();
}
// TableColumnModelListener
@Override
public void columnMarginChanged(ChangeEvent e) {
updateRowHeights();
}
Nota
Como regla general, todos los parámetros en el getXXRendererComponent son estrictamente de solo lectura, las implementaciones no deben cambiar ningún estado de la persona que llama. Actualizar el rowHeight desde el renderizador es incorrecto .
Muchas buenas ideas están relacionadas en otras respuestas y también en las preguntas relacionadas.
Así que aquí es cómo modifiqué tus clases para obtener una tabla con auto-line-heights totalmente funcionales:
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
}
Cambios importantes:
- calcule la altura solo después de que
JFrame
esté visible porque es posible que no tengamos la fuente correcta antes - change
rowHeight
only from event-dispatch-thread (SwingUtilities.isEventDispatchThread()
/SwingUtilities.invokeLater()
); de lo contrario, la tabla podría asignar diferentes valores a nuestroRowHeightCellRenderer
mientras estamos en el medio de calcular la altura -
rowHeight
a 20, para que podamos reducir de nuevo -
TableColumnModelListener
para que sepamos cuándo el usuario cambia el tamaño de una columna y se encoge o crecerowHeight
s (puede quitar el Listener de forma segura si no se desea)
También ahora solo evalúo las filas modificadas para los cambios de 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
Cambios:
-
setSize()
engetTableCellRendererComponent()
ya que de lo contrario el objeto no se puede ajustar correctamente - calcular el
preferedSize
según la posición del último personaje
En realidad, JTextArea ya implementa todas las características necesarias para cambiar dinámicamente su altura en función de su ancho. Uno puede ver esta funcionalidad en acción, cuando JTextArea se usa dentro de un ScrollPane, donde su altura se ajusta automáticamente para ajustarse al ancho del ScrollPane. Para usar esta característica, primero hay que establecer el tamaño de JTextArea con cierto ancho y luego JTextArea # getPreferredSize () devolverá el alto requerido para mostrar el texto (si el ajuste de línea se establece en verdadero).
Entonces, para cambiar dinámicamente la altura de fila de una JTable en función de su ancho, se puede hacer lo siguiente:
- Agregue un TableCellRenderer personalizado a la tabla que devuelve un JTextArea en TableCellRenderer # getTableCellRendererComponent ()
- Escuche el cambio de tamaño de las columnas como se explica aquí: Java JTable detectar Columna re dimensionada por usuario
- Actualice las alturas de fila de las columnas como se explica aquí: Ajuste automático de la altura de las filas en una JTable
- Durante la actualización de las alturas de fila, uno calcula el tamaño requerido de JTextArea configurando primero el nuevo ancho y luego obteniendo la altura preferida como se muestra en el siguiente fragmento de código
Función de actualización de las alturas de fila:
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);
}
}
Con este enfoque, no son necesarios más cálculos de columnas o filas.
Aquí está el código completo del ExampleTable del OP, ajustado a esta implementación.
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);
}
}
Tenga en cuenta que reutilicé y cambié parte del código de Java JTable detect Columna de nuevo tamaño por usuario y Ajusta automáticamente el alto de filas en una JTable .
Me parece más sencillo ajustar la altura de la fila desde dentro del procesador de componentes, así:
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;
}
}
Esto también funcionará para un renderizador que devuelve un JTextPane
.