user5113188
user5113188

Reputation:

Add & Delete Row for JTable of Custom Components

I'm doing step by step...

Changing LookAndFeel of JTable of Custom Component

Note: I have more custom Columns, this code it's for demonstration porpoise.

Now, I want to implement add Row and Delete Row using a class extended from DefaultTableModel, for JTable populated with custom Objects.

Here my code:

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

public class LAF_TableCustom_AddDelRow {

  static JFrame frame = new JFrame();

  public JComponent makeUI() {

    String[] hdrsObjects = {"PanelSpinnerRadioButton Class Column"};
    Object[][] objectMatrix = new Object[3][1];
    objectMatrix[0][0] = new PanelSpinnerRadioButtonData(false, 10, 40);
    objectMatrix[1][0] = new PanelSpinnerRadioButtonData(true,  20, 40);
    objectMatrix[2][0] = new PanelSpinnerRadioButtonData(false, 30, 40);

//    JTable table = new JTable(new DefaultTableModel(objectMatrix, hdrsObjects)) {
    JTable table = new JTable(new PSRBTableModel(objectMatrix, hdrsObjects)) {
      @Override public void updateUI() {
        super.updateUI();
        setRowHeight(30);
        TableColumn tc = getColumn("PanelSpinnerRadioButton Class Column");
        tc.setCellRenderer(new PSRBTableCellRenderer());
        tc.setCellEditor(new PSRBTableCellEditor());
      }
    };

    JScrollPane scrollPane = new JScrollPane(table);
    scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
    scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

    JPanel pV = new JPanel();
    pV.setLayout(new BoxLayout(pV, BoxLayout.PAGE_AXIS));

    JButton bAddRow = new JButton("Add Row");
    bAddRow.addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent evt) {
        ((PSRBTableModel)table.getModel()).addRow(
            new Object[] { new PanelSpinnerRadioButtonData(false, 10, 40) }
        );
//        ((DefaultTableModel)table.getModel()).addRow(
//            new Object[] { new PanelSpinnerRadioButtonData(false, 10, 40) }
//        );
      }
    });

    pV.add(bAddRow);
    pV.add(scrollPane);
    return pV;
  }

  public static void main(String... args) {
    EventQueue.invokeLater(() -> {
    frame.getContentPane().add(new LAF_TableCustom_AddDelRow().makeUI());
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.setSize(320, 240);
    frame.setVisible(true);
    });
  }

}

class PanelSpinnerRadioButtonData {
  private boolean opt02 = false;
  private Integer from = 0;
  private Integer size = 1;

  PanelSpinnerRadioButtonData() {
    this(false, 5, 10);
  }
  PanelSpinnerRadioButtonData(boolean opt02, Integer from, Integer size) {
    this.opt02 = opt02;
    this.from = from;
    this.size = size;
  }
  public boolean getOption() {
    return opt02;
  }
  public Integer getFrom() {
    return from;
  }
  public Integer getSize() {
    return size;
  }
}

class PanelSpinnerRadioButton extends JPanel {
  public final JRadioButton jrbOption01 = new JRadioButton("01");
  public final JRadioButton jrbOption02 = new JRadioButton("12");
  public final JSpinner jspnValues = new JSpinner(new SpinnerNumberModel(5, 0, 10, 1));
  public final JButton jbRemoveRow = new JButton("Del Row");

  private final JPanel panel = new JPanel();

  PanelSpinnerRadioButton() {
    this(new PanelSpinnerRadioButtonData(false, 20, 40));
  }
  PanelSpinnerRadioButton(PanelSpinnerRadioButtonData data) {
    super();

    panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
    panel.add(jrbOption01);
    panel.add(jrbOption02);
    panel.add(jspnValues);
    panel.add(jbRemoveRow);

    jbRemoveRow.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);
        ((PSRBTableModel) table.getModel()).removeRow(row);

      }
    });

    ButtonGroup bg = new ButtonGroup();
    bg.add(jrbOption01);
    bg.add(jrbOption02);
    ((SpinnerNumberModel) jspnValues.getModel()).setMaximum(data.getSize());
    setData(data);

    init();
  }
  private void init() {
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
    setBackground(new Color(0, 0, 0, 0));
    add(panel);
  }
  public void setData(PanelSpinnerRadioButtonData data) {
    if (data.getOption()) {
      jrbOption02.setSelected(true);
    } else {
      jrbOption01.setSelected(true);
    }
    ((SpinnerNumberModel) jspnValues.getModel()).setValue(data.getFrom());
  }
  public PanelSpinnerRadioButtonData getData() {
    return new PanelSpinnerRadioButtonData(
        jrbOption02.isSelected(),
        (Integer) ((SpinnerNumberModel) jspnValues.getModel()).getValue(),
        (Integer) ((SpinnerNumberModel) jspnValues.getModel()).getMaximum());
  }

}

class PSRBTableCellRenderer implements TableCellRenderer {
  private final PanelSpinnerRadioButton renderer = new PanelSpinnerRadioButton();
  @Override public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    if (value instanceof PanelSpinnerRadioButtonData) {
      renderer.setData((PanelSpinnerRadioButtonData) value);
    }
    return renderer;
  }
}

class PSRBTableCellEditor extends AbstractCellEditor implements TableCellEditor {
  private final PanelSpinnerRadioButton editor = new PanelSpinnerRadioButton();
  @Override public Object getCellEditorValue() {
    return editor.getData();
  }
  @Override public Component getTableCellEditorComponent(
      JTable table, Object value, boolean isSelected, int row, int column) {
    if (value instanceof PanelSpinnerRadioButtonData) {
      editor.setData((PanelSpinnerRadioButtonData) value);
    }
    return editor;
  }

}

//class PSRBTableModel extends AbstractTableModel {
class PSRBTableModel extends DefaultTableModel {

  private final Object[][] data;
  private final Object[] columns;

  public PSRBTableModel(Object[][] data, Object[] columns) {
    this.data = data;
    this.columns = columns;
  }

  @Override public Object getValueAt(int rowIndex, int columnIndex) {
    if (data != null) {
      if (data.length > 0) {
        return data[rowIndex][columnIndex];
      }
    }
    return null;
  }

  @Override public int getColumnCount() {
    return ((columns == null) ? 0: columns.length);
  }

  @Override public int getRowCount() {
    return ((data == null) ? 0: data.length);
  }

  @Override public Class getColumnClass(int columnIndex) {
    if (data != null) {
      if (data.length > 0) {
        if (data[0][columnIndex] instanceof PanelSpinnerRadioButton) {
          return PanelSpinnerRadioButton.class;
        }
        return data[0][columnIndex].getClass();
      }
    }
    return Object.class;
  }

  @Override public boolean isCellEditable(int rowIndex, int columnIndex) {
    if (data != null) {
      if (data.length > 0) {
        if (data[0][columnIndex] instanceof PanelSpinnerRadioButton) {
          return true;
        }
      }
    }
    return true;
  }

  @Override public void setValueAt(Object value, int row, int col) {
    data[row][col] = value;
    fireTableCellUpdated(row, col);
  }

  @Override public String getColumnName(int columnIndex) {
    return (String)columns[columnIndex];
  }

  @Override public void removeRow(int row) {
    fireTableRowsDeleted(row, row);
    System.out.println("fireTableRowsDeleted(" + row + ", " + row + ");");
  }

  @Override public void addRow(Object[] rowData) {
    super.addRow(rowData);
  }
}

The problem!!!

When I pressed the Del Row Button, the row wasn't deleted! I got the print message related with the Row: fireTableRowsDeleted(ROW, ROW);I

When I pressed the Add Row I got this Exception...

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 3 > 0
    at java.util.Vector.insertElementAt(Vector.java:598)
    at javax.swing.table.DefaultTableModel.insertRow(DefaultTableModel.java:374)
    at javax.swing.table.DefaultTableModel.addRow(DefaultTableModel.java:350)
    at javax.swing.table.DefaultTableModel.addRow(DefaultTableModel.java:361)
    at PSRBTableModel.addRow(LAF_TableCustom_AddDelRow.java:248)
    at LAF_TableCustom_AddDelRow$2.actionPerformed(LAF_TableCustom_AddDelRow.java:45)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)

How Can I to do these operations (removeRow, addRow), overriding respective methods with my Custom DefaultTableModel?

This answer, is not working

EDIT

  //Doesn't Work
  @Override public void removeRow(int row) {
    super.removeRow(row);
  }

  //Doesn't Work
  @Override public void removeRow(int row) {
    getDataVector().removeElementAt(row);
    fireTableRowsDeleted(row, row);
  }

  //Doesn't Work (Not overriding the Method)
  //  @Override public void removeRow(int row) {
  //    getDataVector().removeElementAt(row);
  //  }

Upvotes: 0

Views: 1208

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347234

The basic problem is, DefaultTableModel is already backed by a Vector, which manages the row/column data. But your extension of it, implements it's own data structure making the Vector redundant.

If you take the time to look at the source code for DefaultTabelModel you'll find that addRow(Object[]) calls addRow(Vector) which makes use of getRowCount, which call your implementation of it...

/**
 *  Adds a row to the end of the model.  The new row will contain
 *  <code>null</code> values unless <code>rowData</code> is specified.
 *  Notification of the row being added will be generated.
 *
 * @param   rowData          optional data of the row being added
 */
public void addRow(Vector rowData) {
    insertRow(getRowCount(), rowData);
}

/**
 *  Adds a row to the end of the model.  The new row will contain
 *  <code>null</code> values unless <code>rowData</code> is specified.
 *  Notification of the row being added will be generated.
 *
 * @param   rowData          optional data of the row being added
 */
public void addRow(Object[] rowData) {
    addRow(convertToVector(rowData));
}

This means that Vector has nothing in it, but your code says that there are 3 rows, which basically triggers all the core issues.

You have a choice to make. Either use DefaultTableModel and throw away you own data structures (data and columns) or use something like AbstractTableModel which will force you to implement your own data structures.

May basic recommendation would be to go for the first, as the DefaultTableModel should be capable of providing the basic underlying requirements you seem to need.

For a, really, basic example:

class PSRBTableModel extends DefaultTableModel {

    public PSRBTableModel(Object[][] data, Object[] columns) {
        super(data, columns);
    }

    @Override
    public Class getColumnClass(int columnIndex) {
        if (getRowCount() > 0) {
            Object value = getValueAt(0, columnIndex);
            if (value instanceof PanelSpinnerRadioButton) {
                return PanelSpinnerRadioButton.class;
            }
        }
        return Object.class;
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return getColumnClass(columnIndex) == PanelSpinnerRadioButton.class;
    }
}

Upvotes: 2

Related Questions