具有多种类型 JComponent (Swing) 的 JPanel 的 JTable

JTable for JPanel with multiple type JComponent (Swing)

这是一道很长的题,但需要所有代码(我想的是一道基础题,没有共同的组成部分)。

我需要构建一个 JTable,其中包含多个自定义类型 JPanel 的多个列(使用 JSlider + JComboBox + JTextField)一栏。

我正在阅读:http://pekalicious.com/blog/custom-jpanel-cell-with-jbuttons-in-jtable/

在这个问题中,我将使用一个列,其中只有一个自定义 JPanel,名称为 PanelSpinnerRadioButton

自定义面板:PanelSpinnerRadioButton.java

public class PanelSpinnerRadioButton extends JPanel {
  private final JRadioButton jrbOption01= new JRadioButton("01");
  private final JRadioButton jrbOption02 = new JRadioButton("12");

  private final JSpinner jspnValues = new JSpinner(new SpinnerNumberModel(5, 0, 10, 1));

  private final JPanel jpPanelSpinnerRadioButton = new JPanel();

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

  PanelSpinnerRadioButton(PanelSpinnerRadioButtonData data) {
    super();

    jpPanelSpinnerRadioButton.setLayout(new BoxLayout(jpPanelSpinnerRadioButton, BoxLayout.LINE_AXIS));
    jpPanelSpinnerRadioButton.add(jrbOption01);
    jpPanelSpinnerRadioButton.add(jrbOption02);
    jpPanelSpinnerRadioButton.add(Box.createRigidArea(new Dimension(5,0)));
    jpPanelSpinnerRadioButton.add(new JSeparator(JSeparator.VERTICAL));
    jpPanelSpinnerRadioButton.add(Box.createRigidArea(new Dimension(5,0)));
    jpPanelSpinnerRadioButton.add(jspnValues);
    //Change States of RadioButtons (will be readOnly, ButtonGroup is not needed)
    jrbOption02.setSelected(data.getOption());
    jrbOption01.setSelected(!data.getOption());
    //Change States of Spinner
    ((SpinnerNumberModel)jspnValues.getModel()).setValue(data.getFrom());
    ((SpinnerNumberModel)jspnValues.getModel()).setMaximum(data.getSize());

    init();
  }

  private void init() {
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
    setBackground(new Color(0, 0, 0, 0/*64*/));
    add(jpPanelSpinnerRadioButton);
  }

  // Used in PSRBTableCellEditor.getCellEditorValue()
  public PanelSpinnerRadioButtonData getData() {
    return new PanelSpinnerRadioButtonData(
        jrbOption02.isSelected(),
        (Integer)((SpinnerNumberModel)jspnValues.getModel()).getValue(),
        (Integer)((SpinnerNumberModel)jspnValues.getModel()).getMaximum()
    );
  }

}

这里是自定义JPanel之前的Image Result

自定义面板的数据类型:PanelSpinnerRadioButtonData.java

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

}

现在编码来处理我的自定义 JPanel(带有内部 JComponents)

Table型号:PSRBTableModel.java

public class PSRBTableModel extends AbstractTableModel {

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

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

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

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

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

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

  public boolean isCellEditable(int rowIndex, int columnIndex) {
    if (data != null) {
      if (data.length > 0) {
        if (data[0][columnIndex] instanceof PanelSpinnerRadioButton) {
          //PanelSpinnerRadioButton Is not Editable
          return false;
        }
      }
    }
    return true;
  }

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

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

}

渲染器:PSRBTableCellRenderer.java

public class PSRBTableCellRenderer implements TableCellRenderer {

  public Component getTableCellRendererComponent(JTable table, Object value,
        boolean isSelected, boolean hasFocus, int row, int column) {

    Object output = null;

    if (value instanceof PanelSpinnerRadioButtonData) {
      output = new PanelSpinnerRadioButton((PanelSpinnerRadioButtonData)value);
    }
    return (Component)output;
  }

}

和 EditorCell:PSRBTableCellEditor.java

public class PSRBTableCellEditor extends AbstractCellEditor implements TableCellEditor {

  protected Object output;

  PSRBTableCellEditor() {
  }

  public Object getCellEditorValue() {
    //Returns the value contained in the editor.
    if (output  instanceof PanelSpinnerRadioButton) {
      return ((PanelSpinnerRadioButton)output).getData();
    }
    return null;
  }

  public Component getTableCellEditorComponent(JTable table, Object value,
      boolean isSelected, int row, int column) {
    //Sets an initial value for the editor.

    if (value instanceof PanelSpinnerRadioButtonData) {
      output = new PanelSpinnerRadioButton((PanelSpinnerRadioButtonData)value);
      return (PanelSpinnerRadioButton)output;
    }
    return null;
  }

}

测试之前的代码(我正在使用 JFrame)

String[] hdrsObjects = new String[]{"PanelSpinnerRadioButton Class Column"};
Object[][] objectMatrix = new Object[3][hdrsObjects.length];

objectMatrix[0][0] = new PanelSpinnerRadioButton();
objectMatrix[1][0] = new PanelSpinnerRadioButton();
objectMatrix[2][0] = new PanelSpinnerRadioButton();

JTable jtbl = new JTable(new PSRBTableModel(objectMatrix, hdrsObjects));
//jtbl.setDefaultRenderer(PanelSpinnerRadioButton.class, new PSRBTableCellRenderer());
//jtbl.setDefaultEditor(PanelSpinnerRadioButton.class, new PSRBTableCellEditor());
jtbl.getColumn("PanelSpinnerRadioButton Class Column").setCellRenderer(new PSRBTableCellRenderer());
jtbl.getColumn("PanelSpinnerRadioButton Class Column").setCellEditor(new PSRBTableCellEditor());

add(new JScrollPane(jtbl));

但是我看不到 JTable 有(3 行自定义 JPanel 'PanelSpinnerRadioButton')

我有这个例外...

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
  at javax.swing.plaf.synth.SynthTableUI.paintCell(SynthTableUI.java:684)
  at javax.swing.plaf.synth.SynthTableUI.paintCells(SynthTableUI.java:580)
  at javax.swing.plaf.synth.SynthTableUI.paint(SynthTableUI.java:364)
  at javax.swing.plaf.synth.SynthTableUI.update(SynthTableUI.java:275)
  at javax.swing.JComponent.paintComponent(JComponent.java:780)
  at javax.swing.JComponent.paint(JComponent.java:1056)
  at javax.swing.JComponent.paintChildren(JComponent.java:889)
  at javax.swing.JComponent.paint(JComponent.java:1065)
  at javax.swing.JViewport.paint(JViewport.java:728)
  at javax.swing.JComponent.paintChildren(JComponent.java:889)
  at javax.swing.JComponent.paint(JComponent.java:1065)
  at javax.swing.JComponent.paintChildren(JComponent.java:889)
  at javax.swing.JComponent.paint(JComponent.java:1065)
  at javax.swing.JComponent.paintChildren(JComponent.java:889)
  at javax.swing.JComponent.paint(JComponent.java:1065)
  at javax.swing.JComponent.paintChildren(JComponent.java:889)
  at javax.swing.JComponent.paint(JComponent.java:1065)
  at javax.swing.JComponent.paintChildren(JComponent.java:889)
  at javax.swing.JComponent.paint(JComponent.java:1065)
  at javax.swing.JComponent.paintChildren(JComponent.java:889)
  at javax.swing.JComponent.paint(JComponent.java:1065)
  at javax.swing.JComponent.paintChildren(JComponent.java:889)
  at javax.swing.JComponent.paint(JComponent.java:1065)
  at javax.swing.JComponent.paintChildren(JComponent.java:889)
  at javax.swing.JComponent.paint(JComponent.java:1065)
  at javax.swing.JComponent.paintChildren(JComponent.java:889)
  at javax.swing.JComponent.paint(JComponent.java:1065)
  at javax.swing.JLayeredPane.paint(JLayeredPane.java:586)
  at javax.swing.JComponent.paintChildren(JComponent.java:889)
  at javax.swing.JComponent.paintToOffscreen(JComponent.java:5217)
  at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1579)
  at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1502)
  at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:306)
  at javax.swing.RepaintManager.paint(RepaintManager.java:1272)
  at javax.swing.JComponent.paint(JComponent.java:1042)
  at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:39)
  at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:79)
  at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:116)
  at java.awt.Container.paint(Container.java:1975)
  at java.awt.Window.paint(Window.java:3912)
  at javax.swing.RepaintManager.run(RepaintManager.java:842)
  at javax.swing.RepaintManager.run(RepaintManager.java:814)
  at java.security.AccessController.doPrivileged(Native Method)
  at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
  at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:814)
  at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:789)
  at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:738)
  at javax.swing.RepaintManager.access00(RepaintManager.java:64)
  at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1732)
  at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
  at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
  at java.awt.EventQueue.access0(EventQueue.java:97)
  at java.awt.EventQueue.run(EventQueue.java:709)
  at java.awt.EventQueue.run(EventQueue.java:703)
  at java.security.AccessController.doPrivileged(Native Method)
  at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
  at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
  at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
  at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
  at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
  at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
  at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
  at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

问题

版本

请考虑这种情况(由相同的 TableCellRenderer PSRBTableCellRenderer 和 TableCellEditor PSRBTableCellEditor 处理的另一个自定义 JPanel)

class PanelButton extends JPanel {

  private final JPanel jpPanelButton = new JPanel();
  // the ActionListener does not matter now
  JButton jbtAction = new JButton("Action");

  PanelButton() {
    this(new PanelButtonEmpty());
  }
  PanelButton(PanelButtonEmpty data) {
    super();
    jpPanelButton.setLayout(new BoxLayout(jpPanelButton, BoxLayout.LINE_AXIS));

    jpPanelButton.add(jbtAction);

    /*Overridable method call in constructor*/
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
    setBackground(new Color(0, 0, 0, 0/*64*/));
    add(jpPanelButton);
  }

  public PanelButtonEmpty getData() {
    return new PanelButtonEmpty();
  }

  public void setData(PanelButtonEmpty data) {
  }
}

我在想一个完全空的 class(将值转换为 TableCellRenderer PSRBTableCellEditor)

class PanelButtonEmpty {
  PanelButtonEmpty() { }
  //public boolean getLazy() { return true; }
  //public void setLazy(boolean b) {}
}

在这种情况下,我有两种类型的自定义 JPanel,因此我有

用于 TableCellEditor

public class PSRBTableCellEditor extends AbstractCellEditor implements TableCellEditor {
  private Object output;
  @Override public Object getCellEditorValue() {
    if (output  instanceof PanelSpinnerRadioButton) {
      return ((PanelSpinnerRadioButton)output).getData();
    }
    if (output  instanceof PanelButton) {
      return ((PanelButton)output).getData();
    }
    return null;
  }

  @Override public Component getTableCellEditorComponent(JTable table, Object value,
      boolean isSelected, int row, int column) {
    if (value instanceof PanelSpinnerRadioButtonData) {
      output = new PanelSpinnerRadioButton((PanelSpinnerRadioButtonData)value);
      return (PanelSpinnerRadioButton)output;
    }
    if (value instanceof PanelButtonEmpty) {
      output = new PanelButton((PanelButtonEmpty)value);
      return (PanelButton)output;
    }
    return null;
  }
}

对于TableCellRenderer

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

问题

如何避免第二次警告(在新场景中)?

如何处理与此处描述不同的对象?

PD:

我不确定将第二部分问题放在另一个具有相同源代码的 post 中...

objectMatrix[0][0] = new PanelSpinnerRadioButton();

  • 应设置 PanelSpinnerRadioButtonDataTableModel,而不是 PanelSpinnerRadioButton.
  • 等组件

output = new PanelSpinnerRadioButton((PanelSpinnerRadioButtonData)value);

  • TableCellRenderer#getTableCellRendererComponent(...).
  • 每次都创建一个组件很浪费
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;

public class TableTest {
  public JComponent makeUI() {
    String[] hdrsObjects = {"PanelSpinnerRadioButton Class Column"};
    Object[][] objectMatrix = new Object[3][hdrsObjects.length];
    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));
    table.setRowHeight(30);

    TableColumn tc = table.getColumn("PanelSpinnerRadioButton Class Column");
    tc.setCellRenderer(new PSRBTableCellRenderer());
    tc.setCellEditor(new PSRBTableCellEditor());

    JPanel p = new JPanel(new BorderLayout());
    p.add(new JScrollPane(table));
    return p;
  }
  public static void main(String... args) {
    EventQueue.invokeLater(() -> {
      try {
        for (UIManager.LookAndFeelInfo laf: UIManager.getInstalledLookAndFeels()) {
          if ("Nimbus".equals(laf.getName())) {
            UIManager.setLookAndFeel(laf.getClassName());
          }
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new TableTest().makeUI());
      f.setSize(240, 240);
      f.setLocationRelativeTo(null);
      f.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));

  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(Box.createRigidArea(new Dimension(5, 0)));
    panel.add(new JSeparator(JSeparator.VERTICAL));
    panel.add(Box.createRigidArea(new Dimension(5, 0)));
    panel.add(jspnValues);
    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());
  }

  // Used in PSRBTableCellEditor.getCellEditorValue()
  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;
  }
}

第二部分

我有这个源代码...

public class PSRBTableCell extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {
  private Object output = null;
  private final PanelSpinnerRadioButton psrb = new PanelSpinnerRadioButton();
  private final PanelButton pb = new PanelButton();

  @Override
  public Object getCellEditorValue() {
    if (output instanceof PanelSpinnerRadioButton) {
      return psrb.getData();
    }
    if (output instanceof PanelButton) {
      return pb.getData();
    }
    return null;
  }

  @Override
  public Component getTableCellEditorComponent(JTable table, Object value,
      boolean isSelected, int row, int column) {
    if (value instanceof PanelSpinnerRadioButtonData) {
      psrb.setData((PanelSpinnerRadioButtonData) value);
      output = psrb;
      return psrb;
    }
    if (value instanceof PanelButtonEmpty) {
      pb.setData((PanelButtonEmpty) value);
      output = pb;
      return pb;
    }
    return null;
  }

  @Override
  public Component getTableCellRendererComponent(JTable table, Object value,
        boolean isSelected, boolean hasFocus, int row, int column) {
    if (value instanceof PanelSpinnerRadioButtonData) {
      psrb.setData((PanelSpinnerRadioButtonData)value);
      output = psrb;
      return psrb;
    }
    if (value instanceof PanelButtonEmpty) {
      pb.setData((PanelButtonEmpty)value);
      output = pb;
      return pb;
    }
    return null;
  }
}