Jake Badlands
Jake Badlands

Reputation: 1026

How to make a JTable column to contain not JTextFields, but JTextAreas?

Please tell me, how to make a JTable column to contain JTextAreas, so the cell's height will increase when user types a lot of text and we can see more than one line (cell gets expanded; as result, row will expand too)

Upvotes: 2

Views: 2271

Answers (2)

Jakub Zaverka
Jakub Zaverka

Reputation: 8874

You need to write your own cell renderer and editor based on JTextArea:

    public class Start
{
    public static class JTextPaneCellEditor extends AbstractCellEditor implements TableCellEditor, KeyListener
    {
        private JViewport viewport;
        private JTable table;
        private int row;
        private JTextPane pane;

        public JTextPaneCellEditor(){
            viewport = new JViewport();
            pane = new JTextPane();
            viewport.add(pane);
            pane.addKeyListener(this);
        }

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

        @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column){
            this.table = table;
            this.row = row;
            pane.setText(value.toString());
            int newHeight = (int)pane.getPreferredSize().getHeight();
            if(table.getRowHeight(row) < newHeight){
                table.setRowHeight(row, newHeight);
            }
            return pane;
        }

        @Override public boolean isCellEditable(EventObject e){
            if (e instanceof MouseEvent) {
                return ((MouseEvent)e).getClickCount() >= 2;
            }
            return true;
        }

        @Override public void keyTyped(KeyEvent e){
            table.setRowHeight(row, (int)pane.getPreferredSize().getHeight());
        }

        @Override public void keyPressed(KeyEvent e){
            if(e.getKeyCode() == KeyEvent.VK_ENTER){
                stopCellEditing();
            }
        }

        @Override public void keyReleased(KeyEvent e){
        }
    }

    private static class JTextPaneCellRenderer extends JViewport implements TableCellRenderer
    {
        JTextPane pane;
        JTextPaneCellRenderer(){
            pane = new JTextPane();
            add(pane);
        }

        @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column){
            pane.setText(value.toString());
            table.setRowHeight(row, (int)pane.getPreferredSize().getHeight());
            return this;
        }
    }

    public static void main(String[] args){
        JTable table = new JTable(new String[][]{{"String String String String StringString String String String StringString String String String StringString String String String StringString String String String String"}, {"String 2"}}, new String[]{"Column"});
        table.setDefaultRenderer(Object.class, new JTextPaneCellRenderer());
        table.setDefaultEditor(Object.class, new JTextPaneCellEditor());

        JFrame test = new JFrame();
        test.add(new JScrollPane(table));
        test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        test.setSize(300, 300);
        test.setLocationRelativeTo(null);
        test.setVisible(true);
    }
}

EDIT: add viewports for better sizing. But the row is still not expanded on first edit. Does anyone have any ideas?

EDIT2: I agree with the comments. The thing you want is possible, but you need untested, hackish custom implementation. You will be better off if you redesigned your layout to exclude such JTable sorcery.

Upvotes: 5

mKorbel
mKorbel

Reputation: 109813

@Jakub Zaverka

I'll delete this answer later

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

public class AutoWrapTest {

    public JComponent makeUI() {
        String[] columnNames = {"TextAreaCellRenderer"};
        Object[][] data = {
            {"123456789012345678901234567890"},
            {"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddx"},
            {"----------------------------------------------0"},
            {">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>|"},};
        TableModel model = new DefaultTableModel(data, columnNames) {

            private static final long serialVersionUID = 1L;

            @Override
            public boolean isCellEditable(int row, int column) {
                return false;
            }
        };
        JTable table = new JTable(model) {

            private static final long serialVersionUID = 1L;

            @Override
            public void doLayout() {
                TableColumn col = getColumnModel().getColumn(0);
                for (int row = 0; row < getRowCount(); row++) {
                    Component c = prepareRenderer(col.getCellRenderer(), row, 0);
                    if (c instanceof JTextArea) {
                        JTextArea a = (JTextArea) c;
                        int h = getPreferredHeight(a) + getIntercellSpacing().height;
                        if (getRowHeight(row) != h) {
                            setRowHeight(row, h);
                        }
                    }
                }
                super.doLayout();
            }            //http://tips4java.wordpress.com/2008/10/26/text-utilities/

            private int getPreferredHeight(JTextComponent c) {
                Insets insets = c.getInsets();
                View view = c.getUI().getRootView(c).getView(0);
                int preferredHeight = (int) view.getPreferredSpan(View.Y_AXIS);
                return preferredHeight + insets.top + insets.bottom;
            }
        };
        table.setEnabled(false);
        table.setShowGrid(false);
        table.setTableHeader(null);
        table.getColumnModel().getColumn(0).setCellRenderer(new TextAreaCellRenderer());
        JScrollPane sp = new JScrollPane(table);
        sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        JPanel p = new JPanel(new BorderLayout());
        p.add(sp);
        return p;
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                createAndShowGUI();
            }
        });
    }

    public static void createAndShowGUI() {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        f.getContentPane().add(new AutoWrapTest().makeUI());
        f.setSize(200, 200);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

class TextAreaCellRenderer extends JTextArea implements TableCellRenderer {

    private static final long serialVersionUID = 1L;
    private final Color evenColor = new Color(230, 240, 255);

    public TextAreaCellRenderer() {
        super();
        setLineWrap(true);
        setWrapStyleWord(true);
        setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        if (isSelected) {
            setForeground(table.getSelectionForeground());
            setBackground(table.getSelectionBackground());
        } else {
            setForeground(table.getForeground());
            setBackground(table.getBackground());
            setBackground((row % 2 == 0) ? evenColor : getBackground());
        }
        setFont(table.getFont());
        setText((value == null) ? "" : value.toString());
        return this;
    }
}

Upvotes: 2

Related Questions