简体   繁体   中英

How do I resize the height of a JTable row as the user types?

I have an editable JTable. As the user types, if the text is longer than the width I need the height to grow. I have set linewrap to true, but it only changes the height after the user presses enter. What am I missing? I have looked at answers that solve the resizing issue (such as this in the cellRenderer, but I need to adjust the height as the user types, not after they are finished typing.

public class EndCycleCellEditor extends AbstractCellEditor implements TableCellEditor, KeyListener  {
    JComponent component;

    private ArrayList<ArrayList<Integer>> rowColHeight = new ArrayList<ArrayList<Integer>>();

    public EndCycleCellEditor(){
        component  = new JTextArea();
        ((JTextArea) component).setWrapStyleWord(true);
        ((JTextArea) component).setLineWrap(true);
        component.addKeyListener(this);
    }

    @Override
    public Object getCellEditorValue() {
        return ((JTextArea) component).getText();
    }

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value,
            boolean isSelected, int row, int column) {
        ((JTextArea)component).setText(value.toString());
        return component;
    }

    @Override
    public void keyTyped(KeyEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void keyPressed(KeyEvent e) {
        // TODO Auto-generated method stub

            }

    @Override
    public void keyReleased(KeyEvent e) {
        if(((JTextArea) component).getText().length() >= 200){
            Toolkit.getDefaultToolkit().beep();
            ((JTextArea)component).setText(((JTextArea)component).getText().substring(0,200) );
        }

    }



}

1) resize this way is possible, but ugly and not user_friendly

2) don't use non_standard hack as MultiLines span in the JTable

3) put JTextArea to the JScrollPane , but then you have to override Scrolling JScrollPane inside another JScrollPane

import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;

class JTableMultiLineSupport extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {

    private static final long serialVersionUID = 1L;
    private JTextArea editor = new JTextArea(4, 10);
    private JScrollPane jsp = new JScrollPane(editor);
    private JTable table;
    private int row;
    private int col;
    private JTextArea renderer = new JTextArea(4, 10);

    public static void main(String[] args) {
        JTable table = new JTable(new String[][]{
                    {"1\n2\n3\n4\n5\n6\n7", "1\n2\n3\n4\n5\n6\n7", "1\n2\n3\n4\n5\n6\n7"},
                    {"1\n2\n3\n4\n5\n6\n7", "1\n2\n3\n4\n5\n6\n7", "1\n2\n3\n4\n5\n6\n7"}},
                new String[]{"First Column", "Second Column", "Third Column"});
        JTableMultiLineSupport mls = new JTableMultiLineSupport();
        table.setDefaultEditor(Object.class, mls);
        table.setDefaultRenderer(Object.class, mls);
        table.setRowHeight(0, 30);
        table.setRowHeight(1, 70);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        JFrame frame = new JFrame("Test");
        frame.add(new JScrollPane(table));
        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value,
            boolean isSelected, int row, int col) {
        this.table = table;
        this.row = row;
        this.col = col;
        editor.setText(value.toString());
        return jsp;
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column) {
        renderer.setText(value == null ? "" : value.toString());
        renderer.setEnabled(false);
        return renderer;
    }

    @Override
    public Object getCellEditorValue() {
        return editor.getText();
    }

    @Override
    public boolean stopCellEditing() {
        table.getModel().setValueAt(editor.getText(), row, col);
        return true;
    }
}

[EDIT: My initial answer had some bugs and implications. Meanwhile, I found a more elegant solution, based on various examples.]

This solution is based on http://www.coderanch.com/t/336033/GUI/java/MultiLine-JTable - I did some bugfixes, though (Mostly: removed the computation of rowHeights from Editor, added computation of rowHeight to Renderer).

The suggestions on http://blog.botunge.dk/post/2009/10/09/JTable-multiline-cell-renderer.aspx are great to make it more beautiful (eg adding the colors and fonts for the table).

Put together, this solution renders JTable cells with multiple lines. The editor automatically updates the row height, when text is entered or deleted. The computation of the row height is left to the preferredSize of the TextArea.

Step one: The CellRenderer

public class MultiLineCellRenderer extends JTextArea implements
    TableCellRenderer {

public MultiLineCellRenderer() {
    setEditable(false);
    setLineWrap(true);
    setWrapStyleWord(true);
}

public Component getTableCellRendererComponent(JTable table, Object value,
        boolean isSelected, boolean hasFocus, int row, int column) {

    if (value instanceof String) {
        setText((String) value);
        // We set the width and force textarea to recompute the preferred height
        setSize(table.getColumnModel().getColumn(column).getWidth(), 1000);

        // we should not do the following in this method.
        // it seems to create an endless loop
        // int rowHeight = table.getRowHeight(row);
        // int cellHeight = getPreferredSize().height;
        // if (cellHeight > rowHeight)
        //  table.setRowHeight(row, cellHeight);
    } else
        setText("");
    return this;
}

/*
 * Make sure to call this method, whenever the table changes.
 * Call it from appropriate TableCellRenderer, TableModelListener, 
 * ComponentListener, TableColumnModelListener.
 */
public void updateRowHeights() {
    for (int row = 0; row < table.getRowCount(); row++) {
        int rowHeight = 0;
        for (int col = 0; col < table.getColumnCount(); col++) {
            Object value = table.getValueAt(row, col);
            if (value != null)
                setText(value.toString());
            else
                setText("");
            setSize(table.getColumnModel().getColumn(col).getWidth(), 1000);
            int cellHeight = getPreferredSize().height;
            if (cellHeight > rowHeight)
                rowHeight = cellHeight;
        }
        table.setRowHeight(row, rowHeight);
    }
}
}

Step 2: The CellEditor (I assume that there is a shorter solution possible, without overriding JTextArea).

public class MultiLineCellEditor extends AbstractCellEditor implements
    TableCellEditor {
MyTextArea textArea;
JTable table;

public MultiLineCellEditor(JTable ta) {
    super();
    table = ta;
    // this component relies on having this renderer for the String
    // class
    MultiLineCellRenderer renderer = new MultiLineCellRenderer();
    table.setDefaultRenderer(String.class, renderer);
    textArea = new MyTextArea();
    textArea.setLineWrap(true);
    textArea.setWrapStyleWord(true);
}

public Object getCellEditorValue() {
    return textArea.getText();
}

public Component getTableCellEditorComponent(JTable table, Object value,
        boolean isSelected, int row, int column) {
    textArea.setText(table.getValueAt(row, column).toString());
    textArea.rowEditing = row;
    textArea.columnEditing = column;
    textArea.lastPreferredHeight = textArea.getPreferredSize().height;
    return textArea;
}

/**
 * This method determines the height in pixel of a cell given the text it
 * contains
 */
private int cellHeight(int row, int col) {
    if (row == table.getEditingRow() && col == table.getEditingColumn())
        return textArea.getPreferredSize().height;
    else
        return table
                .getDefaultRenderer(String.class)
                .getTableCellRendererComponent(table,
                        table.getModel().getValueAt(row, col), false,
                        false, row, col).getPreferredSize().height;
}

void cellGrewEvent(int row, int column) {
    updateRow(row);
}

void cellShrankEvent(int row, int column) {
    updateRow(row);
}

void updateRow(int row) {
    int maxHeight = 0;
    for (int j = 0; j < table.getColumnCount(); j++) {
        int ch;
        if ((ch = cellHeight(row, j)) > maxHeight) {
            maxHeight = ch;
        }
    }
    table.setRowHeight(row, maxHeight);
}

class MyTextArea extends JTextArea implements KeyListener {
    private static final long serialVersionUID = 1L;
    int lastPreferredHeight = 0;
    int rowEditing;
    int columnEditing;

    MyTextArea() {
        addKeyListener(this);
        // This is a fix to Bug Id 4256006
        addAncestorListener(new AncestorListener() {
            public void ancestorAdded(AncestorEvent e) {
                requestFocus();
            }

            public void ancestorMoved(AncestorEvent e) {
            }

            public void ancestorRemoved(AncestorEvent e) {
            }
        });
    }

    public void keyPressed(KeyEvent e) {
    }

    public void keyReleased(KeyEvent e) {
    }

    public void keyTyped(KeyEvent e) {
        if (getPreferredSize().getHeight() > lastPreferredHeight) {
            lastPreferredHeight = getPreferredSize().height;
            cellGrewEvent(rowEditing, columnEditing);
            // this will trigger the addition of extra lines upon the
            // cell growing and prevent all the text being lost when 
            // the cell grows to the point of requiring scrollbars
            table.setValueAt(getText(), rowEditing, columnEditing);
        } else if (getPreferredSize().getHeight() < lastPreferredHeight) {
            lastPreferredHeight = getPreferredSize().height;
            cellShrankEvent(rowEditing, columnEditing);
        } else if (table.getValueAt(rowEditing, columnEditing).equals(""))
            table.setValueAt(getText(), rowEditing, columnEditing);
    }
}
}

And here some code for testing it.

public class MultiLineCellExample extends JFrame {

private static final long serialVersionUID = 1L;

public MultiLineCellExample() {
    DefaultTableModel dm = new DefaultTableModel() {
        private static final long serialVersionUID = 1L;

        public Class<?> getColumnClass(int columnIndex) {
            return String.class;
        }
    };
    dm.setDataVector(
            new Object[][] {
                    { "aa TEST TEST TEST TEST TEST TEST TEST TEST END",
                            "bb", "cc" }, { "A\nA", "B\nB", "C\nC" } },
            new Object[] { "1", "2", "3" });
    JTable table = new JTable(dm);
    MultiLineCellEditor editor = new MultiLineCellEditor(table);
    table.setDefaultEditor(String.class, editor);
    dm.fireTableRowsInserted(0, 0);
    JScrollPane scroll = new JScrollPane(table);
    getContentPane().add(scroll);
}

public static void main(String[] args) {
    MultiLineCellExample mlce = new MultiLineCellExample();

    mlce.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    mlce.setSize(400, 400);
    mlce.pack();
    mlce.setVisible(true);
}

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM