JTable for JPanel with multiple type JComponent (Swing)

北战南征 提交于 2019-12-13 02:41:29

问题


This is a long question, but all code is needed (I was thinking a basic question, with not common components).

I need to build a JTable with multiple Columns with multiple type Custom JPanel (with JSlider + JComboBox + JTextField) in only one Column.

I was reading: http://pekalicious.com/blog/custom-jpanel-cell-with-jbuttons-in-jtable/

In this question, I will use one Column with only one Customized JPanel with name PanelSpinnerRadioButton.

The Custom Panel: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()
    );
  }

}

Here the Image Result of before Custom JPanel

Data Type for Custom Panel: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; }

}

Now the coded to handle my Custom JPanel (with inner JComponents)

the Table Model: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];
  }

}

Now the Renderer: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;
  }

}

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

}

Test the Before Code (I'm working with 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));

But I can't see the JTable with (3 rows with Custom JPanel 'PanelSpinnerRadioButton')

I have this exception...

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$4.run(RepaintManager.java:842)
  at javax.swing.RepaintManager$4.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.access$1200(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.access$500(EventQueue.java:97)
  at java.awt.EventQueue$3.run(EventQueue.java:709)
  at java.awt.EventQueue$3.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)

QUESTION

  • What's wrong?
  • Why I can't see the JTable with 3 PanelSpinnerRadioButton?

EDITION

Please consider this scenario (Another Custom JPanel handled by the same TableCellRenderer PSRBTableCellRenderer and TableCellEditor PSRBTableCellEditor)

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

I was thinking in a totally empty class (to cast the value in TableCellRenderer PSRBTableCellEditor)

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

In this case I have two types of Custom JPanel for this reason I have

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

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

QUESTIONS

How to avoid your Second Warning (in the new scenario)?

  • It is a waste to create each time a component in the TableCellRenderer#getTableCellRendererComponent(...).

How to handle with Objects different to here described?

PD:

I'm not sure to put this second part question in another post with the same source code...


回答1:


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

  • Should be set PanelSpinnerRadioButtonData to the TableModel, rather than a component such as PanelSpinnerRadioButton.

output = new PanelSpinnerRadioButton((PanelSpinnerRadioButtonData)value);

  • It is a waste to create each time a component in the 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;
  }
}



回答2:


For the Second Part

I have this Source Code...

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


来源:https://stackoverflow.com/questions/38137288/jtable-for-jpanel-with-multiple-type-jcomponent-swing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!