java - 具有非均匀对象数组的 JTable,自定义 TableCellRenderer 实现的返回

标签 java swing nullpointerexception tablecellrenderer

我有从 JPanel 扩展的自定义组件。 名称为:PanelButtonPanelSlider

问题 1:

使用非均匀矩阵创建 xxxTableModel(...) 是否有效(或安全)?

String[] hdrsObjects = {"PanelButton Class", "PanelSlider Class"};
Object[][] objectMatrix = new Object[3][2];
objectMatrix[0][0] = new PanelButtonData(...);
objectMatrix[1][0] = new PanelButtonData(...);
objectMatrix[2][0] = new PanelButtonData(...);

// objectMatrix[0][1] = /*Non Assigned*/
objectMatrix[1][1] = new PanelSliderData(0, 20, 40);
objectMatrix[2][1] = new PanelSliderData(30, 40, 60);

JTable Mytable = new JTable(new MyTableModel(objectMatrix, hdrsObjects)) {...}

这相当于 3 行不同长度:

jtblGeneral.setModel(new DefaultTableModel(
  new Object [][] { {"Cell Row:0,Col:0"}, {"Cell Row:1,Col:0", "Cell Row:1,Col:1"}, {"Cell Row:2,Col:0", "Cell Row:2,Col:1"}
  },
  new String [] {
    "Title 1", "Title 2"
  }
));

现在我正在实现我自己的TableCellRenderer

class MyTableCellRenderer implements TableCellRenderer {

  private final PanelSlider ps = new PanelSlider(new PanelSliderData(0, 25, 50));
  private final PanelButton pb = new PanelButton(new PanelButtonData(false));

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

    if (value instanceof PanelButtonData) {
      pb.setData((PanelButtonData) value);
      return pb;
    }

    if (value instanceof PanelSliderData) {
      ps.setData((PanelSliderData) value);
      return ps;
    }

  //if (value != null)
  //  return (Component) value;
  //return this;

  //return null;

  //    return table.getDefaultRenderer(String.class).getTableCellRendererComponent(
  //        table, value, isSelected, hasFocus, row, column);

    return new JLabel();
  }
}

问题 2:

如果之前问题的答案为"is"。当我的自定义 JPanel 类中的值为 null 且未定义(如 row:0、col:1 中的单元格)时,我必须返回什么类型对象?

  • return null; 当我返回 null 时(Nimbus 和 GTK 外观和感觉会出现问题) UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");

这里是异常java.lang.NullPointerException:

  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)
  • 返回新的 JLabel();
  • 返回新的Component();

问题 3:

但是,当值不为 null 并且我不知道 Class 类型时,如何处理返回?

  • if (value != null) return (Component) value;
  • return table.getDefaultRenderer(String.class).getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
  • 扩展我的 ComponentMyTableCellRenderer 类并返回它;

所有代码

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

public class TableButtonSlider extends JFrame {

  public TableButtonSlider()  {
    setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    setSize(600, 300);
    setVisible(true);
    setLocationRelativeTo(null);
  }
  public static void setLAF(Container container, String laf) {
    try {
      UIManager.setLookAndFeel(laf);
    } catch (ClassNotFoundException | InstantiationException
        | IllegalAccessException | UnsupportedLookAndFeelException e) {
    }
    SwingUtilities.updateComponentTreeUI(container);
  }
  static final JFrame frame = new JFrame();
  public JComponent makeUI() {

    String[] hdrsObjects = {"PanelButton Class", "PanelSlider Class"};
    Object[][] objectMatrix = new Object[3][2];
    objectMatrix[0][0] = new PanelButtonData(true);
    objectMatrix[1][0] = new PanelButtonData(false);
    objectMatrix[2][0] = new PanelButtonData(false);

//    objectMatrix[0][1] = new PanelSliderData(10, 30, 40);
    objectMatrix[1][1] = new PanelSliderData(0, 20, 40);
    objectMatrix[2][1] = new PanelSliderData(30, 40, 60);

    JTable Mytable = new JTable(new MyTableModel(objectMatrix, hdrsObjects)) {
      @Override public void updateUI() {
        super.updateUI();
        setRowHeight(30);
        TableColumn tc;

        tc = getColumn("PanelSlider Class");
        tc.setCellRenderer(new MyTableCellRenderer());
        tc.setCellEditor(new MyTableCellEditor());
        tc = getColumn("PanelButton Class");
        tc.setCellRenderer(new MyTableCellRenderer());
        tc.setCellEditor(new MyTableCellEditor());
      }
    };

    JScrollPane scrollPane = new JScrollPane(Mytable);

    JPanel pH = new JPanel();
    pH.setLayout(new BoxLayout(pH, BoxLayout.LINE_AXIS));

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

    JButton bInsert = new JButton("Insert");
    bInsert.addActionListener((ActionEvent e) -> {
      ((MyTableModel)Mytable.getModel()).addRow(
        new Object[] {
          new PanelButtonData(false),
          new PanelSliderData(0, 25, 50)
        }
      );
      Mytable.updateUI();
    });

    JButton bMetal = new JButton("Metal");
    bMetal.addActionListener((ActionEvent) -> {
      setLAF(TableButtonSlider.this, "javax.swing.plaf.metal.MetalLookAndFeel");
    });

    JButton bMotif = new JButton("Motif");
    bMotif.addActionListener((ActionEvent) -> {
      setLAF(TableButtonSlider.this, "com.sun.java.swing.plaf.motif.MotifLookAndFeel");
    });

    JButton bNimbus = new JButton("Nimbus");
    bNimbus.addActionListener((ActionEvent) -> {
      setLAF(TableButtonSlider.this, "javax.swing.plaf.nimbus.NimbusLookAndFeel");
    });

    JButton bMacOS = new JButton("mac");
    bMacOS.addActionListener((ActionEvent evt) -> {
      setLAF(TableButtonSlider.this, "com.apple.laf.AquaLookAndFeel");
    });

    JButton bWindows = new JButton("win");
    bWindows.addActionListener((ActionEvent) -> {
      setLAF(TableButtonSlider.this, "com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
    });

    JButton bLinux = new JButton("lnx");
    bLinux.addActionListener((ActionEvent) -> {
      setLAF(TableButtonSlider.this, "com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
    });

    pH.add(bInsert);
    pH.add(Box.createRigidArea(new Dimension(1,0)));
    pH.add(new JSeparator(JSeparator.VERTICAL));
    pH.add(Box.createRigidArea(new Dimension(1,0)));
    pH.add(bLinux);
    pH.add(bMacOS);
    pH.add(bWindows);
    pH.add(Box.createRigidArea(new Dimension(1,0)));
    pH.add(new JSeparator(JSeparator.VERTICAL));
    pH.add(Box.createRigidArea(new Dimension(1,0)));
    pH.add(bMetal);
    pH.add(bMotif);
    pH.add(bNimbus);

    pV.add(pH);
    pV.add(scrollPane);
    return pV;
  }
  public static void main(String... args) {
    UIManager.put("Slider.paintValue", false);
    EventQueue.invokeLater(() -> {
      TableButtonSlider f = new TableButtonSlider();
      f.getContentPane().add(f.makeUI());
    });
  }
}

class PanelButton extends JPanel {
  JButton jbtWavRow = new JButton();
  private final JPanel panel = new JPanel();
  PanelButton(PanelButtonData data) {
    super();
    panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
    panel.add(Box.createRigidArea(new Dimension(2,0)));
    panel.add(jbtWavRow);
    panel.add(Box.createRigidArea(new Dimension(2,0)));
    jbtWavRow.setFont(new Font("Monospaced", Font.PLAIN, 10));
    setData(data);
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
    add(panel);
  }
  public PanelButtonData getData() {
    return new PanelButtonData(jbtWavRow.getActionCommand().equals("+"));
  }
  public void setData(PanelButtonData data) {
    for (ActionListener al : jbtWavRow.getActionListeners()) {
      jbtWavRow.removeActionListener(al);
    }

    if(data.getIns()) {
      jbtWavRow.setText("Insert");
      jbtWavRow.setActionCommand("+");
      jbtWavRow.addActionListener((ActionEvent e) -> {
        JTable table = (JTable)SwingUtilities.getAncestorOfClass(
            JTable.class, (Component) e.getSource());
        table.getCellEditor().stopCellEditing();
        ((MyTableModel)table.getModel()).addRow(
            new Object[] {
              new PanelButtonData(false),
              new PanelSliderData(0, 25, 50)
            }
        );
        table.updateUI();
      });
    } else {
      jbtWavRow.setText("Remove");
      jbtWavRow.setActionCommand("-");
      jbtWavRow.addActionListener((ActionEvent e) -> {
        JTable table = (JTable) SwingUtilities.getAncestorOfClass(
            JTable.class, (Component) e.getSource());

        int row = table.getEditingRow();
        table.getCellEditor().stopCellEditing();
        ((MyTableModel) table.getModel()).removeRow(row);
        // table.updateUI();
      });
    }
  }
}

class PanelButtonData {
  private boolean add = false;
  PanelButtonData(Boolean add) { this.add = add; }
  public void setIns(Boolean add) { this.add = add; }
  public boolean getIns() { return add; }
}

class PanelSlider extends JPanel {
  private final JSlider jslChanger = new JSlider(SwingConstants.HORIZONTAL);
  private final JPanel panel = new JPanel();
  PanelSlider(PanelSliderData data) {
    super();
    panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
    panel.add(Box.createRigidArea(new Dimension(2,0)));
    panel.add(jslChanger);
    panel.add(Box.createRigidArea(new Dimension(2,0)));
    setData(data);
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
    add(panel);
  }
  public void setData(PanelSliderData data) {
    jslChanger.setMinimum(data.getMin());
    jslChanger.setValue(data.getVal());
    jslChanger.setMaximum(data.getMax());
  }
  // Used in MyTableCellRenderer.getCellEditorValue()
  public PanelSliderData getData() {
    return new PanelSliderData(jslChanger.getMinimum(), jslChanger.getValue(), jslChanger.getMaximum());
  }
}

class PanelSliderData {
  private Integer Min = 0;
  private Integer Val = 25;
  private Integer Max = 50;
  PanelSliderData(int Min, int Val, int Max) {
    this.Min = Min;
    this.Val = Val;
    this.Max = Max;
  }
  public Integer getMin() { return Min; }
  public Integer getVal() { return Val; }
  public Integer getMax() { return Max; }
}

class MyTableCellRenderer implements TableCellRenderer {
  private final PanelSlider ps = new PanelSlider(new PanelSliderData(0, 25, 50));
  private final PanelButton pb = new PanelButton(new PanelButtonData(false));
  @Override public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    if (value instanceof PanelButtonData) {
      pb.setData((PanelButtonData) value);
      return pb;
    }
    if (value instanceof PanelSliderData) {
      ps.setData((PanelSliderData) value);
      return ps;
    }

    //if (value != null)
    //  return (Component) value;
    //return this;

    return null;

    //return table.getDefaultRenderer(String.class).getTableCellRendererComponent(
    //    table, value, isSelected, hasFocus, row, column);

    //return new JLabel();
  }
}

class MyTableCellEditor extends AbstractCellEditor implements TableCellEditor {
  protected Object output;
  private final PanelButton pb = new PanelButton(new PanelButtonData(false));
  private final PanelSlider ps = new PanelSlider(new PanelSliderData(0, 25, 50));
  @Override public Object getCellEditorValue() {
    if (output instanceof PanelButton) {
      return pb.getData();
    }
    if (output instanceof PanelSlider) {
      return ps.getData();
    }
    return null;
  }
  @Override public Component getTableCellEditorComponent(
      JTable table, Object value, boolean isSelected, int row, int column) {
    if (value instanceof PanelButtonData) {
      pb.setData((PanelButtonData) value);
      output = pb;
      return pb;
    }
    if (value instanceof PanelSliderData) {
      ps.setData((PanelSliderData) value);
      output = ps;
      return ps;
    }
    return null;
  }
}

class MyTableModel extends AbstractTableModel {
//class MyTableModel extends DefaultTableModel {
  private Object[][] data;
  private Object[] columns;
  public MyTableModel(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 PanelButton) {
          return PanelButton.class;
        }
        if (data[0][columnIndex] instanceof PanelSlider) {
          return PanelSlider.class;
        }
        //return data[0][columnIndex].getClass();
        return String.class;
      }
    }
    return Object.class;
  }
  @Override public boolean isCellEditable(int rowIndex, int columnIndex) {
    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) {
    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;
    int maxCol;
    if ((data != null) && (data.length > 0)) {
      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];
        }
      }
      maxCol = data[0].length < rowData.length?data[0].length:rowData.length;
    } else {
      newData = new Object[1][rowData.length];
      maxCol = rowData.length;
    }
    //Insert rowData objects
    for (int col = 0; col < maxCol; col++) {
      newData[newData.length - 1][col] = rowData[col];
    }
    data = newData;
  }
}

编辑 1

  public static void main(String... args) {
    UIManager.put("Slider.paintValue", false);
    try {
      UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
    } catch (ClassNotFoundException | InstantiationException
        | IllegalAccessException | UnsupportedLookAndFeelException e) { }

    EventQueue.invokeLater(() -> {
      TableButtonSlider f = new TableButtonSlider();
      f.getContentPane().add(f.makeUI());
    });
  }

建立nimbus LookAndFeel,就像在main方法上的第一个操作一样,引发异常:

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.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.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:3904)
  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:80)
  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:80)
  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)

最佳答案

Is it valid (or secure) to create a xxxTableModel(...) with nonuniform Matrix?

是的,您可以做到这一点,但是您需要付出很多努力才能使其工作,因为 JTable 确实不是为这种方式设计的

If Answer is YES for before Question. When the value is null and not defined (like cell in row:0, col:1) in my custom JPanel Classes, what Type Object I must return?

您必须返回 Component 的实例,不能返回 null

But, When the value Is not null and I don't know the Class type, How handle the return?

这与你的第二个问题的答案相同,你必须返回一个 Component 实例,这取决于你想要如何处理 null 之间的差异和“未知”值

观察结果

深入研究代码后,您对 updateUI 进行了不适当的调用,这并不是在更改 UI 状态时触发 UI“更新”的方法,它只是为了(由系统)用来通知组件外观委托(delegate)已更改

在您的 TableModel 中,您应该在模型更新时触发适当的事件,这将通知父 JTable 并进行适当的更改。

我删除了代码中对 updateUI 的所有调用,只是更新了 addRow 方法来调用 fireTableRowsInserted

public void addRow(Object[] rowData) {
    Object[][] newData;
    int maxCol;
    if ((data != null) && (data.length > 0)) {
        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];
            }
        }
        maxCol = data[0].length < rowData.length ? data[0].length : rowData.length;
    } else {
        newData = new Object[1][rowData.length];
        maxCol = rowData.length;
    }
    //Insert rowData objects
    for (int col = 0; col < maxCol; col++) {
        newData[newData.length - 1][col] = rowData[col];
    }
    data = newData;
    fireTableRowsInserted(data.length - 1, data.length - 1);
}

并且NullPointerException消失了。您还必须对 removeRow 进行适当的更改

关于java - 具有非均匀对象数组的 JTable,自定义 TableCellRenderer 实现的返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45045025/

相关文章:

java - 如何从 Firebase 中一起检索字符串和列表

java - 字符串生成器到 java 中的字符串数组

java - 通用方法中的比较器解决方案

Java : divide the screen

java - 什么是NullPointerException,我该如何解决?

java - Java中有不可变的单链表实现吗?

java - JFrame 可以显示进程吗?

java - JComboBox 弹出菜单不出现

java - Java 空指针异常

Java 7 Update 21 Applet 问题