Reputation: 103
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
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();
}
});
}
}
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();
}
});
}
}
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
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