How to use custom JTable cell editor and cell renderer

55,919

Solution 1

have to add proper LayoutManager, Editable/non_Editable properties for already visible JPanel

let enjoy

import java.awt.*;
import java.awt.event.*;
import java.util.EventObject;
import javax.swing.*;
import javax.swing.table.*;

public class PanelInTable {

    private JFrame frame;
    private JTable compTable = null;
    private PanelTableModel compModel = null;
    private JButton addButton = null;

    public static void main(String args[]) {
        try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
            //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception fail) {
        }
        SwingUtilities.invokeLater(() -> {
            new PanelInTable().makeUI();
        });
    }

    public void makeUI() {
        compTable = CreateCompTable();
        JScrollPane CompTableScrollpane = new JScrollPane(compTable, 
                JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        JPanel bottomPanel = CreateBottomPanel();
        frame = new JFrame("Comp Table Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(CompTableScrollpane, BorderLayout.CENTER);
        frame.add(bottomPanel, BorderLayout.SOUTH);
        frame.setPreferredSize(new Dimension(800, 400));
        frame.setLocation(150, 150);
        frame.pack();
        frame.setVisible(true);
    }

    public JTable CreateCompTable() {
        compModel = new PanelTableModel();
        compModel.addRow();
        JTable table = new JTable(compModel);
        table.setRowHeight(new CompCellPanel().getPreferredSize().height);
        table.setTableHeader(null);
        PanelCellEditorRenderer PanelCellEditorRenderer = new PanelCellEditorRenderer();
        table.setDefaultRenderer(Object.class, PanelCellEditorRenderer);
        table.setDefaultEditor(Object.class, PanelCellEditorRenderer);
        return table;
    }

    public JPanel CreateBottomPanel() {
        addButton = new JButton("Add Comp");
        addButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                Object source = ae.getSource();
                if (source == addButton) {
                    compModel.addRow();
                }
            }
        });
        JPanel panel = new JPanel(new GridBagLayout());
        panel.add(addButton);
        return panel;
    }
}

class PanelCellEditorRenderer extends AbstractCellEditor implements 
        TableCellRenderer, TableCellEditor {

    private static final long serialVersionUID = 1L;
    private CompCellPanel renderer = new CompCellPanel();
    private CompCellPanel editor = new CompCellPanel();

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, 
            boolean isSelected, boolean hasFocus, int row, int column) {
        renderer.setComp((Comp) value);
        return renderer;
    }

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, 
            boolean isSelected, int row, int column) {
        editor.setComp((Comp) value);
        return editor;
    }

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

    @Override
    public boolean isCellEditable(EventObject anEvent) {
        return true;
    }

    @Override
    public boolean shouldSelectCell(EventObject anEvent) {
        return false;
    }
}

class PanelTableModel extends DefaultTableModel {

    private static final long serialVersionUID = 1L;

    @Override
    public int getColumnCount() {
        return 1;
    }

    public void addRow() {
        super.addRow(new Object[]{new Comp(0, 0, "", "")});
    }
}

class Comp {

    public int type;
    public int relation;
    public String lower;
    public String upper;

    public Comp(int type, int relation, String lower, String upper) {
        this.type = type;
        this.relation = relation;
        this.lower = lower;
        this.upper = upper;
    }
}

class CompCellPanel extends JPanel {

    private static final long serialVersionUID = 1L;
    private JLabel labelWith = new JLabel("With ");
    private JComboBox typeCombo = new JComboBox(new Object[]
    {"height", "length", "volume"});
    private JComboBox relationCombo = new JComboBox(new Object[]
    {"above", "below", "between"});
    private JTextField lowerField = new JTextField();
    private JLabel labelAnd = new JLabel(" and ");
    private JTextField upperField = new JTextField();
    private JButton removeButton = new JButton("remove");

   public CompCellPanel() {
        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
        relationCombo.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                enableUpper(relationCombo.getSelectedIndex() == 2);
            }
        });
        enableUpper(false);
        removeButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                JTable table = (JTable) SwingUtilities.getAncestorOfClass(
                        JTable.class, (Component) e.getSource());
                int row = table.getEditingRow();
                table.getCellEditor().stopCellEditing();
                ((DefaultTableModel) table.getModel()).removeRow(row);
            }
        });
        add(labelWith);
        add(typeCombo);
        add(relationCombo);
        add(lowerField);
        add(labelAnd);
        add(upperField);
        add(Box.createHorizontalStrut(100));
        add(removeButton);
    }

    private void enableUpper(boolean enable) {
        labelAnd.setEnabled(enable);
        upperField.setEnabled(enable);
    }

    public void setComp(Comp Comp) {
        typeCombo.setSelectedIndex(Comp.type);
        relationCombo.setSelectedIndex(Comp.relation);
        lowerField.setText(Comp.lower);
        upperField.setText(Comp.upper);
        enableUpper(Comp.relation == 2);
    }

    public Comp getComp() {
        return new Comp(typeCombo.getSelectedIndex(), 
                relationCombo.getSelectedIndex(), 
                lowerField.getText(), upperField.getText());
    }
}

Solution 2

I would strongly suggest to reuse the functionality made available in the default table renderers and editors, as there are many things wrong with your code

  1. Please split your editor, renderer and table model. Having them all in the same class is just weird
  2. For your renderer, do not create new instances of a Component each time. Instead, reuse the same component and just modify that Component in the getTableCellRendererComponent method
  3. Same goes for the editor Component
  4. Extend a default editor instead of implementing the methods with UnsupportedOperationExceptions or by just returning empty Strings

To back-up my forth point, a little quote from the Editors part in the JTable tutorial:

What if you want to specify an editor other than a text field, check box, or combo box? As DefaultCellEditor does not support other types of components, you must do a little more work. You need to create a class that implements the TableCellEditor interface. The AbstractCellEditor class is a good superclass to use. It implements TableCellEditor's superinterface, CellEditor, saving you the trouble of implementing the event firing code necessary for cell editors.

Your cell editor class needs to define at least two methods — getCellEditorValue and getTableCellEditorComponent. The getCellEditorValue method, required by CellEditor, returns the cell's current value. The getTableCellEditorComponent method, required by TableCellEditor, should configure and return the component that you want to use as the editor.

As clearly explained there you must implement the event firing code:

saving you the trouble of implementing the event firing code necessary for cell editors.

which you clearly neglected. Therefore my advise to start from AbstractCellEditor instead of implementing the interface from scratch

Share:
55,919
Harsha
Author by

Harsha

I am a BCS post graduate level student and working as a java programmer in Kandy, Sri Lanka. I've fallen love with java so much.

Updated on January 20, 2020

Comments

  • Harsha
    Harsha over 4 years

    I have created a JTable with a custom table render and custom cell editor which gives the result in the image

    enter image description here

    I created the panel shown in the first table cells using a separate class which extended JPanel. and add table values as,

            tbl.setCellEditor(new customCell());
            tbl.getColumnModel().getColumn(0).setCellRenderer(new customCell());
    
            DefaultTableModel dtm = (DefaultTableModel) tbl.getModel();
    
            Vector v = new Vector();
            v.add(new Panel());
            v.add("Test");
            dtm.addRow(v);
    
            v.clear();
            v.add(new Panel());
            v.add("Test 2");
            dtm.addRow(v);
    

    And this is my table custom class to create this table,

    class customCell extends DefaultTableModel implements TableCellRenderer, TableCellEditor {
    
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
                Panel p = new Panel();            
                table.setRowHeight(row, p.getHeight());
                return p;
            }
    
            public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
    
                return new Panel();
            }
    
            public Object getCellEditorValue() {
                return "";
            }
    
            public boolean isCellEditable(EventObject anEvent) {
                throw new UnsupportedOperationException("Not supported yet.");
            }
    
            public boolean shouldSelectCell(EventObject anEvent) {
                return true;
            }
    
            @Override
            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return true;
            }
    
            public boolean stopCellEditing() {
                return true;
            }
    
            public void cancelCellEditing() {
                throw new UnsupportedOperationException("Not supported yet.");
            }
    
            public void addCellEditorListener(CellEditorListener l) {
                throw new UnsupportedOperationException("Not supported yet.");
            }
    
            public void removeCellEditorListener(CellEditorListener l) {
                throw new UnsupportedOperationException("Not supported yet.");
            }
        }
    

    My problem is thought the panel is shown as I expected I can't type into the text field or change check box or click the button. please tell me how to solve this.

  • kleopatra
    kleopatra almost 12 years
    stopping the edit belongs into the editor, not into the panel. Also, messing up the model from the outside actionListener is ... arguable ;-)
  • mKorbel
    mKorbel almost 12 years
    thank you for great catch, btw this code is revised by your honesty more than 3 times, I tried move and to use Action from JTable made by Rob(I think that nothing better, free and simpler around), no changes and event are fired quite similair,
  • Radu Simionescu
    Radu Simionescu over 10 years
    why are you extending AbstractCellEditor
  • mKorbel
    mKorbel over 10 years
    @Radu Simionescu because ....... ice bear??? :-), I'm see that as easiest, compact for code example in SSCCE forn, sticked to public forum
  • bmurauer
    bmurauer over 8 years
    please use lower case variable names, this is very confusing.
  • partho
    partho over 7 years