Question

EDIT

I've tried changing the table model back to DefaultTableModel but I am getting an Exception when compiling my code and I can't figure out why!

Here's my table init:

jTable1.setModel(new Table1Model());
jTable1.setDefaultRenderer(Color.class,new ColorRenderer(true));
jTable1.getColumnModel().getColumn(5).setCellEditor(new ColorEditor());

My class extending the model:

class Table1Model extends DefaultTableModel {
    //private String[] columnNames = {"Station #",
    private Object[] columnNames = {"Station #",
                                    "Name",
                                    "avg Time",
                                    "Buffer",
                                    "Buffer Parts",
                                    "Color"};
    private Object[][] data = {
    {"1", "Station 1",
     new Integer(10), false, new Integer(0), Color.red},
    {"2", "Station 2",
     new Integer(10), false, new Integer(0), Color.blue},
    {"3", "Station 3",
     new Integer(10), false, new Integer(0), Color.green},
    {"4", "Station 4",
     new Integer(10), false, new Integer(0), Color.orange},
    {"5", "Station 5",
     new Integer(10), false, new Integer(0), Color.black}
    };


    @Override
    public int getColumnCount() {
        return columnNames.length;
    }

   @Override
    public int getRowCount() {
       //int length = data.length;
       //int datalength = Integer.parseInt(length);
       return data.length;
    }

    @Override
    public String getColumnName(int col) {
        return columnNames[col].toString();
    }

    @Override
    public Object getValueAt(int row, int col) {
        return data[row][col];
    }

    /*
     * JTable uses this method to determine the default renderer/
     * editor for each cell.  If we didn't implement this method,
     * then the last column would contain text ("true"/"false"),
     * rather than a check box.
     */
    @Override
    public Class getColumnClass(int c) {
        return getValueAt(0, c).getClass();
    }

    /*
     * Don't need to implement this method unless your table's
     * editable.
     */
    @Override
    public boolean isCellEditable(int row, int col) {
        //Note that the data/cell address is constant,
        //no matter where the cell appears onscreen.
        if (col == 0) { return false; }
        else if (col == 4) { 
            boolean di = (Boolean) getValueAt(row,(col-1));
            if (!di) { return false; }
            else { return true; }
        }
        else { return true; }
    }

    /*
     * Don't need to implement this method unless your table's
     * data can change.
     */
    @Override
    public void setValueAt(Object value, int row, int col) {
        data[row][col] = value;
        fireTableCellUpdated(row, col);
    }

   /*public void removeRow(int row) {
        data.removeRow(row);
    }*/


    private void printDebugData() {
        int numRows = getRowCount();
        int numCols = getColumnCount();

        for (int i=0; i < numRows; i++) {
            System.out.print("    row " + i + ":");
            for (int j=0; j < numCols; j++) {
                System.out.print("  " + data[i][j]);
            }
            System.out.println();
        }
        System.out.println("--------------------------");
    }
}

This generates the following error:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at SimGui$Table1Model.getRowCount(SimGui.java:863)
at javax.swing.table.DefaultTableModel.setDataVector(DefaultTableModel.java:224)
at javax.swing.table.DefaultTableModel.<init>(DefaultTableModel.java:124)
at javax.swing.table.DefaultTableModel.<init>(DefaultTableModel.java:106)
at javax.swing.table.DefaultTableModel.<init>(DefaultTableModel.java:86)
at SimGui$Table1Model.<init>(SimGui.java:832)
at SimGui.initComponents(SimGui.java:265)
at SimGui.<init>(SimGui.java:34)
at SimGui$16.run(SimGui.java:789)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:701)
at java.awt.EventQueue.access$000(EventQueue.java:102)
at java.awt.EventQueue$3.run(EventQueue.java:662)
at java.awt.EventQueue$3.run(EventQueue.java:660)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:671)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:244)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:163)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:147)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:139)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:97)

Can you help me figure out what's wrong? Also, will I be able to use the ColorEditor with the DefaultTableModel?

Was it helpful?

Solution

You have two obvious choices here: either give your class a getVectorData() method or else give it another similarly useful method that allows you to extract the nucleus of your data from your model. You probably shouldn't use an AbstractTableModel variable though but rather use a variable of your own custom type that extends AbstractTableModel to allow you to be able to call custom methods of your model.

i.e.,

MyTableModel model = (MyTableModel)jTable1.getModel();
SomeCollection myKeyData = model.getMyKeyData();

Also, this statement:

I have recently created my own class to extend AbstractTableModel to be able to insert some logic on isCellEditable and setValueAt.

Doesn't make sense to me since you could always use a DefaultTableModel and simply override those two methods. But if you use a DefaultTableModel, don't also have it hold the 2D array of object as you're trying to do. Rather feed the data into its internal data via the appropriate constructor or via its addRow(...) method. Else you lose all the power that DefaultTableModel has to offer.

Edit
If you want to use a DefaultTableModel to leverage its methods, then you cannot use a separate data "nucleus" for your model (here your Object[][]), but instead must load your data into the model that is held inside of the DefaultTableModel super class. This can be done either via the correct super constructor or by adding rows of data using its addRow(...) method.

For example, here I load your data into a DefaultTableModel override:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;

import javax.swing.*;
import javax.swing.table.*;

public class TableModelTest extends JPanel {
   private static final Object[][] DATA = {
         { "1", "Station 1", new Integer(10), false, new Integer(0), Color.red },
         { "2", "Station 2", new Integer(10), false, new Integer(0), Color.blue },
         { "3", "Station 3", new Integer(10), false, new Integer(0),
               Color.green },
         { "4", "Station 4", new Integer(10), false, new Integer(0),
               Color.orange },
         { "5", "Station 5", new Integer(10), false, new Integer(0),
               Color.black } };
   private MyTableModel myTableModel = new MyTableModel(DATA);
   private JTable table = new JTable(myTableModel);

   public TableModelTest() {
      setLayout(new BorderLayout());
      add(new JScrollPane(table), BorderLayout.CENTER);

      table.getColumnModel().getColumn(5)
            .setCellRenderer(new ColorCellRenderer());
      table.getColumnModel().getColumn(5).setCellEditor(new ColorCellEditor());
   }

   private static void createAndShowGui() {
      TableModelTest mainPanel = new TableModelTest();

      JFrame frame = new JFrame("TableModelTest");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class ColorCellEditor extends AbstractCellEditor implements TableCellEditor {

   Color currentColor;
   JButton button;
   JColorChooser colorChooser;
   JDialog dialog;
   protected static final String EDIT = "edit";

   public ColorCellEditor() {
      ActionListener actionListener = new MyActionListener();
      button = new JButton();
      button.setActionCommand(EDIT);
      button.addActionListener(actionListener);
      button.setBorderPainted(false);

      colorChooser = new JColorChooser();
      dialog = JColorChooser.createDialog(button, "Pick a Color", true,
            colorChooser, actionListener, null);
   }

   private class MyActionListener implements ActionListener {

      public void actionPerformed(ActionEvent e) {
         if (EDIT.equals(e.getActionCommand())) {
            button.setBackground(currentColor);
            colorChooser.setColor(currentColor);
            dialog.setVisible(true);

            fireEditingStopped();

         } else {
            currentColor = colorChooser.getColor();
         }
      }
   }

   public Object getCellEditorValue() {
      return currentColor;
   }

   public Component getTableCellEditorComponent(JTable table, Object value,
         boolean isSelected, int row, int column) {
      currentColor = (Color) value;
      return button;
   }
}

class ColorCellRenderer implements TableCellRenderer {
   private static final int IMG_WIDTH = 70;
   private static final int IMG_HEIGHT = 20;
   private JLabel label = new JLabel();

   @Override
   public Component getTableCellRendererComponent(JTable table, Object value,
         boolean arg2, boolean arg3, int arg4, int arg5) {
      Color color = (Color) value;
      BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_HEIGHT,
            BufferedImage.TYPE_INT_RGB);
      Graphics g = img.getGraphics();
      g.setColor(color);
      g.fillRect(0, 0, IMG_WIDTH, IMG_HEIGHT);
      g.dispose();
      ImageIcon icon = new ImageIcon(img);
      label.setIcon(icon);
      return label;
   }

}

class MyTableModel extends DefaultTableModel {
   private static final String[] COLUMN_NAMES = { "Station #", "Name",
         "avg Time", "Buffer", "Buffer Parts", "Color" };

   public MyTableModel(Object[][] data) {
      super(data, COLUMN_NAMES);
   }

   @Override
   public boolean isCellEditable(int row, int col) {

      if (col == 0) {
         return false;
      } else if (col == 4) {
         boolean di = (Boolean) getValueAt(row, (col - 1));
         if (!di) {
            return false;
         } else {
            return true;
         }
      } else {
         return true;
      }
   }

   public void printDebugData() {
      int numRows = getRowCount();
      int numCols = getColumnCount();

      for (int i = 0; i < numRows; i++) {
         System.out.print("    row " + i + ":");
         for (int j = 0; j < numCols; j++) {
            Object datum = getValueAt(i, j);
            // System.out.print("  " + data[i][j]);
            System.out.print("  " + datum);
         }
         System.out.println();
      }
      System.out.println("--------------------------");
   }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top