How to set the RowHeight dynamically in a JTable

后端 未结 5 1436
感动是毒
感动是毒 2020-12-09 20:39

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

5条回答
  •  甜味超标
    2020-12-09 21:05

    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:

    • calculate height only after JFrame is visible because we may not have the correct font before then
    • change rowHeight 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 height
    • initalize rowHeight to 20, so we can shrink again
    • TableColumnModelListener 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 correctly
    • calculate preferedSize according to position of last character

提交回复
热议问题