为自定义组件的 JTable 添加和删除行

Add & Delete Row for JTable of Custom Components

我在一步步做...

注意: 我有更多自定义栏目,此代码用于演示海豚。

现在,我想使用从 DefaultTableModel 扩展而来的 class 实现添加行和删除行,因为 JTable 填充了自定义对象。

这是我的代码:

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);
  }
}

问题!!!

当我按下 Del Row 按钮时,该行没有被删除! 我收到与行相关的打印消息:fireTableRowsDeleted(ROW, ROW);I

当我按下 Add Row 时,我得到了这个异常...

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.actionPerformed(LAF_TableCustom_AddDelRow.java:45)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)

我如何执行这些操作(removeRow、addRow),用我的自定义 DefaultTableModel 覆盖相应的方法?

这个 answer 不起作用

编辑

  //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);
  //  }

基本问题是,DefaultTableModel 已经由管理 row/column 数据的 Vector 支持。但是你对它的扩展,实现了它自己的数据结构,使得 Vector 变得多余。

如果您花时间查看 DefaultTabelModel 的源代码,您会发现 addRow(Object[]) 调用 addRow(Vector),后者使用 getRowCount,后者调用你的实施...

/**
 *  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));
}

这意味着Vector里面什么都没有,但是你的代码说有3行,这基本上触发了所有核心问题。

你可以做出选择。要么使用 DefaultTableModel 并丢弃你自己的数据结构(datacolumns),要么使用类似 AbstractTableModel 的东西,这将迫使你实现自己的数据结构。

可能的基本建议是第一个,因为 DefaultTableModel 应该能够提供您似乎需要的基本基本要求。

举一个非常基本的例子:

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;
    }
}

从您自己的代码开始:

正在编辑 PSRBTableModel class。

  private Object[][] data; //Can't be final, you are changing!!!

现在方法:

  @Override 
  public void removeRow(int row) {
    Object[][] newData = new Object[data.length - 1][data[0].length];
    int rown = 0;
    for (int row1 = 0; row1 <data.length; row1++) {
      if (row1 != row) {
        for (int col = 0; col < data[0].length; col++) {
          newData[rown][col] = data[row1][col];
        }
        rown++;
      }
    }
    data = newData;
  }

  @Override 
  public void addRow(Object[] rowData) {
    Object[][] newData = new Object[data.length + 1][data[0].length];
    for (int row = 0; row <data.length; row++) {
      for (int col = 0; col < data[0].length; col++) {
        newData[row][col] = data[row][col];
      }
    }
    int maxCol = data[0].length < rowData.length?data[0].length:rowData.length;
    for (int col = 0; col < maxCol; col++) {
      newData[data.length][col] = rowData[col];
    }
    data = newData;
  }

根据您的要求调整您的代码(检查一些空对象以避免异常),我不知道您如何使用!!

现在, 如果您要从 AbstractTableModel 扩展 PSRBTableModel class 删除 @Override 标签

class PSRBTableModel extends AbstractTableModel { 

但是,如果您的 PSRBTableModel class 是从 DefaultTableModelclass 扩展而来的,请离开他们!

class PSRBTableModel extends DefaultTableModel {

在你的 LAF_TableCustom_AddDelRow class:

    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) }
        );
        table.updateUI();
      }
    });

在你的PanelSpinnerRadioButtonclass

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