user3463568
user3463568

Reputation: 103

java - JTable cell/column that allows me to enter only single digit number

java - I want a JTable cell/column that allows me to enter only single digit number(like 1 to 9). How can I do that?

I have tried this but the number i pressed is coming twice in the cell.

table.addKeyListener(new KeyAdapter() {         
        public void keyPressed(KeyEvent e) {
            System.out.println("pressed..."+e.getKeyChar());
            char key = e.getKeyChar();
            int selectedColumn = table.getSelectedColumn();
            int selectedRow = table.getSelectedRow();
            if(table.getValueAt(selectedRow, selectedColumn) == null)
            table.setValueAt(key, selectedRow, selectedColumn);
        }
   });

Upvotes: 3

Views: 6296

Answers (2)

Paul Samsotha
Paul Samsotha

Reputation: 208964

Don't use a KeyListener. What you can do instead is use a JTextField for the TableCellEditor and just add a DocumentFilter to the JTextField that allows only numbers.

Here's a running example

import javax.swing.DefaultCellEditor;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.table.TableCellEditor;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.DocumentFilter.FilterBypass;

public class JTableNumberColumn {

    public JTableNumberColumn() {
        JFrame frame = new JFrame();
        JTextField field = createTextField();
        frame.add(new JScrollPane(createTable(field)));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private JTextField createTextField() {
        JTextField field = new JTextField();
        ((AbstractDocument) field.getDocument()).setDocumentFilter(new DocumentFilter() {
            @Override
            public void insertString(FilterBypass fb, int off, String str, AttributeSet attr)
                    throws BadLocationException {
                fb.insertString(off, str.replaceAll("\\D++", ""), attr);  // remove non-digits
            }

            @Override
            public void replace(FilterBypass fb, int off, int len, String str, AttributeSet attr)
                    throws BadLocationException {
                fb.replace(off, len, str.replaceAll("\\D++", ""), attr);  // remove non-digits
            }
        });
        return field;
    }

    private JTable createTable(final JTextField field) {
        String[] cols = {"Only Numbers", "Col 2", "Col 3"};
        String[][] data = {{null, null, null}, {null, null, null}, {null, null, null}};
        final TableCellEditor editor = new DefaultCellEditor(field);
        JTable table = new JTable(data, cols) {
            @Override
            public TableCellEditor getCellEditor(int row, int column) {
                int modelColumn = convertColumnIndexToModel(column);

                if (modelColumn == 0) {
                    return editor;
                } else {
                    return super.getCellEditor(row, column);
                }
            }
        };
        return table;
    }

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

EDIT 1

I may have misread your question. If you want to allow only one number. then you can use a JFormattedTextField with a MaskFormatter as the TableCellEditor. Here's an example

import javax.swing.DefaultCellEditor;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.TableCellEditor;
import javax.swing.text.MaskFormatter;

public class JTableNumberColumn {

    public JTableNumberColumn() {
        JFrame frame = new JFrame();
        JFormattedTextField field = createFormattedTextField();
        frame.add(new JScrollPane(createTable(field)));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }


    private JFormattedTextField createFormattedTextField() {
        JFormattedTextField field = new JFormattedTextField(createFormatter("#"));
        return field;
    }

    protected MaskFormatter createFormatter(String s) {
        MaskFormatter formatter = null;
        try {
            formatter = new MaskFormatter(s);
        } catch (java.text.ParseException exc) {
            System.err.println("formatter is bad: " + exc.getMessage());
            System.exit(-1);
        }
        return formatter;
    }

    private JTable createTable(final JFormattedTextField field) {
        String[] cols = {"Only Numbers", "Col 2", "Col 3"};
        String[][] data = {{null, null, null}, {null, null, null}, {null, null, null}};
        final TableCellEditor editor = new DefaultCellEditor(field);
        JTable table = new JTable(data, cols) {
            @Override
            public TableCellEditor getCellEditor(int row, int column) {
                int modelColumn = convertColumnIndexToModel(column);

                if (modelColumn == 0) {
                    return editor;
                } else {
                    return super.getCellEditor(row, column);
                }
            }
        };
        return table;
    }

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

EDIT 2

It has been brought to my attention I also overlooked the fact that you only want 1-9 and not 0-9. In that case I would just stick with the first option of the JTextField with DocumentFilter, but in the filter, check the length of the input and change the regex to allow only 1-9.

Here's the example

import javax.swing.DefaultCellEditor;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.table.TableCellEditor;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.DocumentFilter.FilterBypass;

public class JTableNumberColumn {

    public JTableNumberColumn() {
        JFrame frame = new JFrame();
        JTextField field1 = createTextField();
        frame.add(new JScrollPane(createTable(field1)));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private JTextField createTextField() {
        final JTextField field = new JTextField();
        ((AbstractDocument) field.getDocument()).setDocumentFilter(new DocumentFilter() {
            @Override
            public void insertString(FilterBypass fb, int off, String str, AttributeSet attr)
                    throws BadLocationException {
                int length = field.getDocument().getLength();
                if (length + str.length() <= 1) {
                    fb.insertString(off, str.replaceAll("[^1-9]", ""), attr);  // remove non-digits
                }
            }

            @Override
            public void replace(FilterBypass fb, int off, int len, String str, AttributeSet attr)
                    throws BadLocationException {
                int length = field.getDocument().getLength();
                if (length + str.length() <= 1) {
                    fb.replace(off, len, str.replaceAll("[^1-9]", ""), attr);  // remove non-digits
                }
            }
        });
        return field;
    }


    private JTable createTable(final JTextField field) {
        String[] cols = {"Only Numbers", "Col 2", "Col 3"};
        String[][] data = {{null, null, null}, {null, null, null}, {null, null, null}};
        final TableCellEditor editor = new DefaultCellEditor(field);
        JTable table = new JTable(data, cols) {
            @Override
            public TableCellEditor getCellEditor(int row, int column) {
                int modelColumn = convertColumnIndexToModel(column);

                if (modelColumn == 0) {
                    return editor;
                } else {
                    return super.getCellEditor(row, column);
                }
            }
        };
        return table;
    }

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

Upvotes: 5

timbo
timbo

Reputation: 1543

One method is to implement your own TableModel (I would do this anyway) and implement the setValueAt(Object, int, int) method. Verify within that method and just don't set the value if it is bad (including putting up a JDialog or whatever is appropriate). Below is some code:

public class MyTableModel extends AbstractTableModel {

...

@Override
public void setValueAt(Object o, int i, int j) {
    if (o instanceof String) {
        char c = ((String) o).charAt(0);

        if (((String) o).length() == 1 && c >= '0' && c <= '9') {
            data[i][j] = Integer.valueOf(((String) o).substring(0, 1));
            return;
        }
    }
    JOptionPane.showMessageDialog(null, "Must input a number from 0-9");
}

I took so long to hit post that somebody else answered! Anyway, because my approach is different, I thought I'd include it. I'm putting in a working program.

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

public class TestTable {

    public static void main(String[] args) {
        TestTable tt = new TestTable();
        tt.init();
    }

    private void init() {
        // Make model
        MyModel model = new MyModel ();
        JTable table = new JTable(model);

        // Add to a frame
        JScrollPane pane = new JScrollPane(table);
        JFrame frame = new JFrame("Test");
        frame.add(pane);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

}

class MyModel extends AbstractTableModel {

    // Just some dummy data
    Integer[][] data = new Integer[10][6];

    // Init the dummy data
    public MyModel() {
        super();
        for (int i = 0; i < data.length; i++) {
            Integer[] is = data[i];
            for (int j = 0; j < is.length; j++) {
                data[i][j] = 20 * i + j;
            }
        }
    }

    @Override
    public int getRowCount() {
        return data.length;
    }

    @Override
    public int getColumnCount() {
        return data[0].length;
    }

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

    // Makes cells editable
    @Override
    public boolean isCellEditable(int i, int j) {
        return true;
    }

    @Override
    public void setValueAt(Object o, int i, int j) {
        if (o instanceof String) {
            char c = ((String) o).charAt(0);

            if (((String) o).length() == 1 && c >= '0' && c <= '9') {
                data[i][j] = Integer.valueOf(((String) o).substring(0, 1));
                return;
            }
        }
        JOptionPane.showMessageDialog(null, "Must input a number from 0-9");
    }
}

Upvotes: 0

Related Questions