setvalueat setmodel componentes autoresizemode java swing jtable

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 nuestro RowHeightCellRenderer 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 crece rowHeight 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() en getTableCellRendererComponent() 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:

  1. Agregue un TableCellRenderer personalizado a la tabla que devuelve un JTextArea en TableCellRenderer # getTableCellRendererComponent ()
  2. Escuche el cambio de tamaño de las columnas como se explica aquí: Java JTable detectar Columna re dimensionada por usuario
  3. Actualice las alturas de fila de las columnas como se explica aquí: Ajuste automático de la altura de las filas en una JTable
  4. 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 .