Java 添加&;删除自定义组件JTable的行

Java 添加&;删除自定义组件JTable的行,java,swing,jtable,defaulttablemodel,Java,Swing,Jtable,Defaulttablemodel,我正在一步一步地做 注意:我有更多自定义列,此代码用于演示海豚 现在,我想使用一个从DefaultTableModel扩展的类来实现addrow和deleteow,该类用于填充自定义对象的JTable 这是我的代码: import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.table.*; public class LAF_TableCustom_AddDelRow { s

我正在一步一步地做

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

现在,我想使用一个从
DefaultTableModel
扩展的类来实现addrow和deleteow,该类用于填充自定义对象的
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(行,行)I

当我按下
添加行
时,我得到了这个异常

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)
如何使用自定义的
DefaultTableModel
覆盖相应的方法来执行这些操作(removeRow、addRow)

这是行不通的

编辑

  //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
已经由一个管理行/列数据的
向量支持。但是您对它的扩展,实现了它自己的数据结构,使得
向量
冗余

如果您花时间查看
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
并抛弃您自己的数据结构(
data
columns
),要么使用类似于
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;
    }
}

基本问题是,
DefaultTableModel
已经由一个管理行/列数据的
向量支持。但是您对它的扩展,实现了它自己的数据结构,使得
向量
冗余

如果您花时间查看
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
并抛弃您自己的数据结构(
data
columns
),要么使用类似于
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

  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;
  }
但是,如果您的
PSRBTableModel
类是从
DefaultTableModelclass
扩展而来的,请离开它们

class PSRBTableModel extends DefaultTableModel {
在您的
LAF\u TableCustom\u AddDelRow
类中:

    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();
      }
    });
面板SpinnerRadioButton
类中

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

从您自己的代码开始:

编辑
PSRBTableModel

  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;
  }
但是,如果您的
PSRBTableModel
类是从
DefaultTableModelclass
扩展而来的,请离开它们

class PSRBTableModel extends DefaultTableModel {
在您的
LAF\u TableCustom\u AddDelRow
类中:

    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();
      }
    });
面板SpinnerRadioButton
类中

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

在调用
fireTableRowsDeleted
之前,实际上需要从底层数据结构中删除行,为此,我建议使用某种
列表
,而不是简单得多的数组。如果您想在模型中拥有自己的数据结构,那么您需要停止使用父类提供的功能,或者利用父类已经提供的支持class@MadProgrammer我编辑了我的问题,添加了其他尝试…在您调用
fireTableRowsDeleted
之前,实际上,您需要从底层数据结构中删除行,为此,我建议使用某种类型的
列表
,而不是数组,这要简单得多。如果您想在模型中拥有自己的数据结构,那么您需要停止使用父类提供的功能,或者利用父类已经提供的支持class@MadProgrammer我编辑了我的问题,添加了另一个尝试…感谢您的时间,在您的代码中添加行,我需要对我的
getValueAt
getColumnCount
getRowCount
setValueAt
getColumnName
,进行注释,但是
Del Row
,仍然不起作用。请查看,不要让我操作控件、Jspinner、JRadioButtons。感谢您抽出时间,让您的代码
添加Row
起作用,我需要对我的
getValueAt
getColumnCount
getRowCount
setValueAt
getColumnName
,进行注释,但是
Del Row
,仍然不起作用。请查看,不要让我操作控件、Jspinner、JRadioButtons。