4

I deal with numeric data that is often edited up or down by 0.01*Value_of_variable, so a spinner looks like a good choice compared to a usual text cell.

I've looked at DefaultCellEditor but it will only take text fields, combo boxes or check boxes.

Is there a convenient way to use a spinner?

akf
  • 38,619
  • 8
  • 86
  • 96
Uri
  • 88,451
  • 51
  • 221
  • 321
  • Thanks folks. I figured that I could create a custom component, I just (wrongly) assumed there might be a different way. – Uri Dec 10 '09 at 22:22

4 Answers4

4

Here's an example that addresses the issue I commented on camickr's answer. This is a complete and compilable example. Take what you need and ditch what you don't.

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

public class JSpinnerInTables {
    static String[] columnNames = {
        "Name","Value"
    };
    static Object[][] data = {
        {"one",1.0},
        {"two",2.0}
    };
    public static void main( String[] args ) {
        JFrame frame = new JFrame();
        JTable table = new JTable(data,columnNames);
        //table.setSurrendersFocusOnKeystroke(true);
        TableColumnModel tcm = table.getColumnModel();
        TableColumn tc = tcm.getColumn(1);
        tc.setCellEditor(new SpinnerEditor());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(table);
        frame.pack();
        frame.setVisible(true);
    }
    public static class SpinnerEditor extends DefaultCellEditor
    {
        JSpinner spinner;
        JSpinner.DefaultEditor editor;
        JTextField textField;
        boolean valueSet;

        // Initializes the spinner.
        public SpinnerEditor() {
            super(new JTextField());
            spinner = new JSpinner();
            editor = ((JSpinner.DefaultEditor)spinner.getEditor());
            textField = editor.getTextField();
            textField.addFocusListener( new FocusListener() {
                public void focusGained( FocusEvent fe ) {
                    System.err.println("Got focus");
                    //textField.setSelectionStart(0);
                    //textField.setSelectionEnd(1);
                    SwingUtilities.invokeLater( new Runnable() {
                        public void run() {
                            if ( valueSet ) {
                                textField.setCaretPosition(1);
                            }
                        }
                    });
                }
                public void focusLost( FocusEvent fe ) {
                }
            });
            textField.addActionListener( new ActionListener() {
                public void actionPerformed( ActionEvent ae ) {
                    stopCellEditing();
                }
            });
        }

        // Prepares the spinner component and returns it.
        public Component getTableCellEditorComponent(
            JTable table, Object value, boolean isSelected, int row, int column
        ) {
            if ( !valueSet ) {
                spinner.setValue(value);
            }
            SwingUtilities.invokeLater( new Runnable() {
                public void run() {
                    textField.requestFocus();
                }
            });
            return spinner;
        }

        public boolean isCellEditable( EventObject eo ) {
            System.err.println("isCellEditable");
            if ( eo instanceof KeyEvent ) {
                KeyEvent ke = (KeyEvent)eo;
                System.err.println("key event: "+ke.getKeyChar());
                textField.setText(String.valueOf(ke.getKeyChar()));
                //textField.select(1,1);
                //textField.setCaretPosition(1);
                //textField.moveCaretPosition(1);
                valueSet = true;
            } else {
                valueSet = false;
            }
            return true;
        }

        // Returns the spinners current value.
        public Object getCellEditorValue() {
            return spinner.getValue();
        }

        public boolean stopCellEditing() {
            System.err.println("Stopping edit");
            try {
                editor.commitEdit();
                spinner.commitEdit();
            } catch ( java.text.ParseException e ) {
                JOptionPane.showMessageDialog(null,
                    "Invalid value, discarding.");
            }
            return super.stopCellEditing();
        }
    }
}
Jason
  • 11,709
  • 9
  • 66
  • 82
2

Simply extend DefaultCellEditor and overwrite the getTableCellEditorComponent() method to return a JSpinner.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • +1 @Uri - Also, see the "Using Other Editors" http://java.sun.com/docs/books/tutorial/uiswing/components/table.html#editrender – ssakl Dec 10 '09 at 17:32
2

... and overwrite the getCellEditorValue() method:

class SpinnerEditor extends DefaultCellEditor
{
    private JSpinner spinner;

    public SpinnerEditor()
    {
        super( new JTextField() );
        spinner = new JSpinner(new SpinnerNumberModel(0, 0, 100, 5));
        spinner.setBorder( null );
    }

    public Component getTableCellEditorComponent(
        JTable table, Object value, boolean isSelected, int row, int column)
    {
        spinner.setValue( value );
        return spinner;
    }

    public Object getCellEditorValue()
    {
        return spinner.getValue();
    }
}
camickr
  • 321,443
  • 19
  • 166
  • 288
  • This solution has some behaviors that may not be desirable for many users. When a user types a number before the editor starts editing, the JTable will focus the JSpinner, not the editor in the JSpinner. This means the text the user just typed goes no where. Contrast this to the way the DefaultCellEditor behaves: the newly typed key is inserted into the cell after the current value. – Jason Jan 27 '12 at 03:57
  • -1 invalid cellEditor implementation because it doesn't comply to its notification contract – kleopatra Jan 28 '12 at 11:40
1

Jason's answer is perfect. To help others that might be looking for a Time and Date version, I've edited Jason's code to suit. Hope it helps someone as Jason's has helped me.

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

public class SpinnerInJTable {
static String[] columnNames = {
    "Name","Time & Date"
};
static Object[][] data = {
    {"Date One",new Date(Long.decode("1397091313404"))},
    {"Date Two", new Date(Long.decode("1397001313404"))}
};
public static void main( String[] args ) {
    JFrame frame = new JFrame();
    JTable table = new JTable(data,columnNames);
    //table.setSurrendersFocusOnKeystroke(true);
    TableColumnModel tcm = table.getColumnModel();
    TableColumn tc = tcm.getColumn(1);
    tc.setCellEditor(new SpinnerEditor());
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(table);
    frame.pack();
    frame.setVisible(true);
}
public static class SpinnerEditor extends DefaultCellEditor
{
    JSpinner spinner;
    JSpinner.DefaultEditor editor;
    JTextField textField;
    boolean valueSet;

    // Initializes the spinner.
    public SpinnerEditor() {
        super(new JTextField());
        spinner = new JSpinner(new SpinnerDateModel());
        editor = ((JSpinner.DateEditor)spinner.getEditor());
        textField = editor.getTextField();
        textField.addFocusListener( new FocusListener() {
            public void focusGained( FocusEvent fe ) {
                System.err.println("Got focus");
                //textField.setSelectionStart(0);
                //textField.setSelectionEnd(1);
                SwingUtilities.invokeLater( new Runnable() {
                    public void run() {
                        if ( valueSet ) {
                            textField.setCaretPosition(1);
                        }
                    }
                });
            }
            public void focusLost( FocusEvent fe ) {
            }
        });
        textField.addActionListener( new ActionListener() {
            public void actionPerformed( ActionEvent ae ) {
                stopCellEditing();
            }
        });
    }

    // Prepares the spinner component and returns it.
    public Component getTableCellEditorComponent(
        JTable table, Object value, boolean isSelected, int row, int column
    ) {
        if ( !valueSet ) {
            spinner.setValue(value);
        }
        SwingUtilities.invokeLater( new Runnable() {
            public void run() {
                textField.requestFocus();
            }
        });
        return spinner;
    }

    public boolean isCellEditable( EventObject eo ) {
        System.err.println("isCellEditable");
        if ( eo instanceof KeyEvent ) {
            KeyEvent ke = (KeyEvent)eo;
            System.err.println("key event: "+ke.getKeyChar());
            textField.setText(String.valueOf(ke.getKeyChar()));
            //textField.select(1,1);
            //textField.setCaretPosition(1);
            //textField.moveCaretPosition(1);
            valueSet = true;
        } else {
            valueSet = false;
        }
        return true;
    }

    // Returns the spinners current value.
    public Object getCellEditorValue() {
        return spinner.getValue();
    }

    public boolean stopCellEditing() {
        System.err.println("Stopping edit");
        try {
            editor.commitEdit();
            spinner.commitEdit();
        } catch ( java.text.ParseException e ) {
            JOptionPane.showMessageDialog(null,
                "Invalid value, discarding.");
        }
        return super.stopCellEditing();
    }
}

}