I've got a Swing screen that opens with a JTable full of itenms and a JList that is empty. After an event from a button I move the itens from JTable to JList, making JTable empty and JList full of items as I desire. But I've got a weird problem that JTable goes back to its original state with all the items again when the frame is minimized and then maximized.
It should not happen as I remove the items from the array that keeps the JTable values. I used sysout to watch on console the array size as the items are being removed and I am sure in the end its size has come to zero.
Plus, I put break points in the getXXX that retrieves the array value to JTable and a repaint() method I put myself to overwrite its original and I got no break point pause of any of them.
So....I don't know where it is getting the value to reset to original state!
Finally, I've just noticed it happens when the table's area is clicked, so change the JFrame to a JDialog where there is no minimize/maximize button would not solve the problem at all.
I don't know if some code would help but in any case, I've got an init method just to initialize the JTable.
private void initTable(Object rowData[][]) {
documents = rowData;
dataModel = new DataModel(rowData, COLUMNS);
scrollPane = new JScrollPane();
scrollPane.setBounds(13, 188, 300, 148);
contentPane.add(scrollPane);
table = new JTable(dataModel) {
#Override
public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {
super.changeSelection(rowIndex, columnIndex, true, false);
}
};
scrollPane.setViewportView(table);
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.getColumnModel().getColumn(0).setPreferredWidth(30);
table.getColumnModel().getColumn(1).setPreferredWidth(260);
table.setCellSelectionEnabled(true);
table.setRowSelectionAllowed(false);
table.setColumnSelectionAllowed(false);
ListSelectionModel cellSelectionModel = table.getSelectionModel();
cellSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
cellSelectionModel.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
int selectedRow = table.getSelectedRow();
int selectedColumn = table.getSelectedColumn();
if (selectedRow >= 0) {
if (selectedColumn == 0) {
Boolean valorCol= (Boolean) documents[selectedRow][0];
if (valorCol== true) {
documents[selectedRow][0] = false;
}
else {
documents[selectedRow][0] = true;
}
}
}
}
table.clearSelection();
}
});
}
_______EDITING_______
The way I remove the items are made of a List not the original array. After the procedure that moves the items from the JTable, I call the method above but this time the list is empty. Here it goes the "missing code"
After moving all the items...
Object[][] documentsMoved = buildTableDataFromList(myVO.getDocuments());
initTable(documentsMoved );
private Object[][] buildTableDataFromList(List<MyVO> list) {
Object[][] retorno = new Object[list.size()][COLUMNS.length];
for (int i = 0; i < list.size(); i++) {
MyVO vo = lista.get(i);
retorno[i][CHECK_COL] = Boolean.TRUE;
retorno[i][DOC_COL] = vo.getFileName();
}
return retorno;
}
It appears you are changing the dataVector of the DataModel directly. This is not the way you should be doing things. What you should do is change cell values through the interface of DataModel (using model indexes) or through the JTable interface (using view indexes).
So my advice would be to change your program to do what I just told. However if you insist on working the way you are you can signal the DataModel about changes in its underlying dataVector, if your DataModel extends DefaultTableModel which I assume it does or implements the AbstractTableModel interface. Call documents.fireTableDataChanged(); after your changes to the whole model, or more granular when changing cells using documents.fireTableCellUpdated(rowModelId,colModelId);.
Another mistake you are making is mixing up view indexes and model indexes. These can be different if rows are sorted and/or columns moved around in your table. Before indexing the model with indexes returned from the view, you should be converting these view indexes with JTable.convertRowIndexToModel and JTable.convertColumnIndexToModel.
Applying this to your selectionListener:
What you should be doing:
cellSelectionModel.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
int selectedRow = table.getSelectedRow();
int selectedColumn = table.getSelectedColumn();
if (selectedRow >= 0) {
if (table.convertColumnIndexToModel(selectedColumn) == 0) {
Boolean valorCol = (Boolean) table.getValueAt(selectedRow,selectedColumn);
if (valorCol == true) {
table.setValueAt(Boolean.FALSE,selectedRow,selectedColumn);
}
else {
table.setValueAt(Boolean.TRUE,selectedRow,selectedColumn);
}
}
}
}
table.clearSelection();
}
});
If you insist on working the way you are:
cellSelectionModel.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
int selectedRow = table.getSelectedRow();
int selectedColumn = table.getSelectedColumn();
if (selectedRow >= 0) {
int rowModelId = table.convertRowIndexToModel(selectedRow);
int colModelId = table.convertColumnIndexToModel(selectedColumn);
if (colModelId == 0) {
Boolean valorCol= (Boolean) documents[rowModelId][0];
if (valorCol== true) {
documents[rowModelId][0] = false;
}
else {
documents[rowModelId][0] = true;
}
dataModel.fireTableCellUpdated(rowModelId,0);
}
}
}
table.clearSelection();
}
});
Alas I think with your understanding of swing and JTable, there are likely more problems in your code.
Related
How can I prevent strings in a JTable and allow and show only numbers?
like for example I press "a" on my keyboard I won't not even that "a" will be displayed in the JTable cell. literally nothing should happen unless a user types in a number. so how can I prevent even not showing "a" ?
I had a similar issue some time ago and solved by validating with an KeyListener. This is a dirty way of doing it, but it works. The only weakness is if you're trying to edit a lot of cells quickly if you're a fast writer. Anyhow, here's the code that worked for me. I've added some commentary, but in short; we're overriding the normal validation and check with a TextField KeyListener if the given key is the one we allow in the TextField. If we allow the key, we enable TextField editing, if not, we turn it off to prevent the character being printed in the TextField. I hope this helps you.
UPDATE 1:
adding a celleditor on the TestField to prevent premature data insertion.
public class TableValidation extends JFrame
{
public static void main(String args[])
{
TableValidation x = new TableValidation();
x.setVisible(true);
}
JPanel topPanel;
JTable table = new JTable();
JScrollPane scrollPane;
String[] columnNames;
String[][] dataValues;
public TableValidation()
{
this.setTitle("JTable Cell Validation");
this.setDefaultCloseOperation (EXIT_ON_CLOSE);
this.setSize(300,112);
// make our panel to tin the table to
topPanel = new JPanel();
topPanel.setLayout(new BorderLayout());
this.getContentPane().add(topPanel);
// set some initial data for the table
columnNames = new String[] {"Anything" ,"Numbers only"};
dataValues = new String[][] { {"h4x0r","1337"} };
table.setRowHeight(50);
table.setModel( new CustomTableModel(dataValues, columnNames) );
TableColumn tableColumn = table.getColumnModel().getColumn(1); // apply our validation to the 2nd column
JTextField textfield = new JTextField(); // the textbox to which we test our validation
// setup our validation system. were passing the textfield as out celleditor source
tableColumn.setCellEditor(new MyCellEditor(textfield));
table.setCellSelectionEnabled(true);
scrollPane = new JScrollPane(table);
topPanel.add(scrollPane,BorderLayout.CENTER);
textfield.addKeyListener(new KeyAdapter()
{
public void keyTyped(KeyEvent e)
{
// check what keys can pass our test
if (textfield.isFocusOwner())
if (e.getKeyChar() != KeyEvent.VK_BACK_SPACE) // we allow backspace, obviously
if (!Character.isDigit(e.getKeyChar())) // if key is not a digit.. cancel editing
{
// when it detects an invalid input, set editable to false. this prevents the input to register
textfield.setEditable(false);
textfield.setBackground(Color.WHITE);
return;
}
textfield.setEditable(true);
}
});
}
}
class MyCellEditor extends AbstractCellEditor implements TableCellEditor
{
private static final long serialVersionUID = 1L;
private JTextField textField;
public MyCellEditor(JTextField textField)
{
this.textField=textField;
}
#Override
public boolean isCellEditable(EventObject e)
{
if (super.isCellEditable(e)) {
if (e instanceof MouseEvent) {
MouseEvent me = (MouseEvent) e;
return me.getClickCount() >= 2;
}
if (e instanceof KeyEvent) {
KeyEvent ke = (KeyEvent) e;
return ke.getKeyCode() == KeyEvent.VK_F2;
}
}
return false;
}
#Override
public Object getCellEditorValue()
{
return this.textField.getText();
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
{
this.textField.setFont(table.getFont());
this.textField.setText(value.toString());
return this.textField;
}
}
class CustomTableModel extends DefaultTableModel
{
CustomTableModel(String[][] data,String[] names)
{
super(data, names);
}
// we always pass true in our tablemodel so we can validate somewhere else
public boolean isCellEditable(int row,int cols)
{
return true;
}
}
I have a jtable with the first column having jbuttons. However when i try to click the button nothing happens. Hovering over the button also doesn't change it's shade to show that it's clickable..
I am running this from within a Java Applet.
I am using the Button Column Class from here:
http://www.camick.com/java/source/ButtonColumn.java
and here is the code i inserted myself
tablemodel = new DefaultTableModel();
//PnlThinClientTable.COLUMNS is an array of strings with the titles of the columns
tablemodel.setColumnIdentifiers(PnlThinClientTable.COLUMNS);
JTable table = new JTable(tablemodel);
table.setEnabled(false);
table.setDefaultRenderer(table.getColumnClass(5), new CustomTblCellRenderer());
table.setBackground(Color.WHITE);
Action wakeUpRow = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e){
JTable table = (JTable)e.getSource();
int modelRow = Integer.valueOf( e.getActionCommand() );
System.out.println("Action Performed");
}
};
// Main.hm_language.get(Language.WAKE_ON_LAN) returns the title of the column i'm interested in
table.getColumn(Main.hm_language.get(Language.WAKE_ON_LAN)).setCellRenderer(new ButtonColumn(table,wakeUpRow,0));
table.getColumn(Main.hm_language.get(Language.WAKE_ON_LAN)).setCellEditor(new ButtonColumn(table, wakeUpRow, 0));
Thanks to #alex2410 for the solution
I had to make sure the cell was Editable
this can be done by either extending the Table upon declaration and overriding the isCellEditable(int row, int col): boolean method,
or in my case I overrode isCellEditable(EventObject e):boolean in the Cell Editor which I apply to the column,
hence the snippet within the Cell Editor I am using would be
#Override
public boolean isCellEditable(EventObject e){
return true;
}
This is as all cells to which the editor is applied need to be editable, as they are all buttons in my case.
Answering to the comment of "how to make 1st column editable" here's how
class MyTableModel extends AbstractTableModel {
public boolean isCellEditable(int row, int col) {
if (col == 1) {
return true;
} else {
return false;
}
}
}
Anyway I leave How to Use Tables Documentation in case it's needed.
And also this post that could help: How to make a table (Jtable) not editable
This question already has answers here:
How to make a JTable non-editable
(7 answers)
Closed 9 years ago.
I have a table which is clickable but when I double click, instead of doing what it is told, it goes editing mode. I have tried isCellEditable() method with no success. Maybe I am doing something wrong?
Here is the code:
public AllResultsFromDB(GUI x) {
final Vector columnNames = new Vector();
final Vector data = new Vector();
for (int i = 1; i <= columns; i++) {
columnNames.addElement(metad.getColumnName(i));
}
// This loop gets the data inside the rows
while (rset.next()) {
final Vector row = new Vector(columns);
for (int i = 1; i <= columns; i++) {
row.addElement(rset.getObject(i));
}
data.addElement(row);
//data.addElement(b);
}
rset.close();
stmt.close();
connection.close();
// Create table with results
final JTable table = new JTable(data, columnNames) {
public boolean isCellEditable() {
return false;
}
public Class getColumnClass(int column) {
for (int row = 0; row < getRowCount(); row++) {
Object obj = getValueAt(row, column);
if (obj != null) {
return obj.getClass();
}
}
return Object.class;
}
};
JScrollPane scroll = new JScrollPane(table);
getContentPane().add(scroll);
JPanel panel = new JPanel();
getContentPane().add(panel, BorderLayout.SOUTH);
table.addMouseListener(new MouseListener() {
public void mousePressed(MouseEvent e) {
//System.out.println(table.getSelectedRow());
}
public void mouseReleased(MouseEvent e) {
//System.out.println(table.getSelectedRow());
}
public void mouseEntered(MouseEvent e) {
//System.out.println(table.getSelectedRow());
}
public void mouseExited(MouseEvent e) {
//System.out.println(table.getSelectedRow());
}
public void mouseClicked(MouseEvent e) {
if(e.getClickCount()==2){
System.out.println(table.getSelectedRow());
}
}
});
The method isCellEditable that you tried to override has a different signature which is:
public boolean isCellEditable(int row, int column)
How could you specify which specific cell otherwise? Next time adding an #Override annotation should help spotting this.
In any case this is not the correct way to make a JTable non-editable. The correct way is to provide a custom AbstractTableModel which returns false with its isCellEditable method. The JTable shouldn't decide if a cell is editable, it's duty of the model to decide it: indeed the isCellEditable method of JTable just asks to its model if the cell is editable. The JTable shows the content, nothing more, it's the model that decides and contains the data.
Since you seem to use just basic features of a JTable you don't need to roll your own table model, a DefaultTableModel will work for you, and you can overrite its isCellEditable method.
You're not that far off.
The actual method isCellEditable takes two parameters, so your method isn't actually overriding anything.
See: http://docs.oracle.com/javase/7/docs/api/javax/swing/table/TableModel.html#isCellEditable%28int,%20int%29
for the correct method.
I am using a JTable whose TableModel is periodically updated through fireTableDataChanged(). These changes are usually pretty small, such as a single row added or modified, however I can't predict where it will happen.
Is there a way to know which rows have been added or modified on a fireTableDataChanged() ? I would like to highlight these rows so the user will know as well.
First off, you must setup your context as appropriate for Swing: the TableModel must have enough knowledge/control about itself to fully comply to its notification contract. That is it must fire row-/cellUpdated or rowsInserted whenever such a change happens.
Then the basic approach to highlight changes (for a certain time) in the JTable is to
implement a custom renderer that decorates cells which are in some storage
configure the table with the custom renderer
listen to changes of the model
add the changeEvents (or a custom object with its relevant properties) to the storage that the renderer knows about
use timers to remove the change markers after some time
SwingX simplifies (biased me :-) the rendering part by providing Highlighters and HighlightPredicates: the former do custom visual decorations when the latter decides they should be turned on. The above approach would be adjusted to
configure the table with highlighters for visual decoration
listen to changes in the model
add the changed cell to a custom HighlightPredicate and configure the Highlighter with it
use timers to remove the change markers after some time
Below is some code, the management of the timers/predicates factored into a class called ChangeDecorator: it keeps one Highlighter for decorating updated cells and one for decorating inserted rows (Note: this is an example, obviously the logic must be extended to cover updated rows :) It's fed by a modelListener with changes and updates the predicates as needed.
JXTable table = new JXTable(model);
final ChangeDecorator controller = new ChangeDecorator();
table.addHighlighter(controller.getChangeHighlighter());
TableModelListener l = new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
if (TableUtilities.isUpdate(e)) {
Change change = new Change(e.getFirstRow(), e.getColumn());
controller.addChange(change);
} else if (TableUtilities.isInsert(e)) {
Change change = new Change(e.getFirstRow());
controller.addChange(change);
}
}
};
model.addTableModelListener(l);
/**
* Manages the Highlighters for inserted rows/updated cells.
*/
public static class ChangeDecorator {
private List<Change> changes;
private AbstractHighlighter update;
private AbstractHighlighter insert;
private Highlighter compound;
public ChangeDecorator() {
changes = new ArrayList<>();
}
public Highlighter getChangeHighlighter() {
if (compound == null) {
update = new ColorHighlighter(new ChangePredicate(changes, true),
Color.YELLOW, null);
insert = new ColorHighlighter(new ChangePredicate(changes, false),
Color.GREEN, null);
compound = new CompoundHighlighter(update, insert);
}
return compound;
}
public void addChange(Change change) {
startTimer(change, change.isCell ? update : insert);
}
private void startTimer(final Change change, final AbstractHighlighter hl) {
changes.add(change);
hl.setHighlightPredicate(new ChangePredicate(changes, change.isCell));
ActionListener l = new ActionListener() {
boolean done;
#Override
public void actionPerformed(ActionEvent e) {
if (!done) {
done = true;
return;
}
((Timer) e.getSource()).stop();
changes.remove(change);
hl.setHighlightPredicate(new ChangePredicate(changes, change.isCell));
}
};
Timer timer = new Timer(2000, l);
timer.setInitialDelay(100);
timer.start();
}
}
/**
* A predicate enables highlighting a cell if it
* contains a change for that cell.
*/
public static class ChangePredicate implements HighlightPredicate {
private List<Change> changes;
private boolean matchCell;
public ChangePredicate(List<Change> changes, boolean matchCell) {
this.changes = new ArrayList(changes);
this.matchCell = matchCell;
}
#Override
public boolean isHighlighted(Component renderer,
ComponentAdapter adapter) {
return changes.contains(createChange(adapter));
}
private Change createChange(ComponentAdapter adapter) {
int modelRow = adapter.convertRowIndexToModel(adapter.row);
if (matchCell) {
int modelColumn =
adapter.convertColumnIndexToModel(adapter.column);;
return new Change(modelRow, modelColumn);
}
return new Change(modelRow);
}
}
/**
* A crude class encapsulating a cell change.
*
*/
public static class Change {
int row;
int column;
boolean isCell;
public Change(int row) {
this(row, -1, false);
}
public Change(int row, int col) {
this(row, col, true);
}
private Change(int row, int col, boolean update) {
this.row = row;
this.column = col;
this.isCell = update;
}
#Override
public boolean equals(Object obj) {
if (!(obj instanceof Change)) return false;
Change other = (Change) obj;
return row == other.row && column == other.column && isCell == other.isCell;
}
}
Can I make my textField in JTable acts like a cell in Excel?
Clear the text when typing in but can editing when get into the cell.
I think these 2 operations will goes to the same event. Am I wrong?
I try to use the keyPressed but nothing work. TT-TT
Here is my code
private JTable getTblMaster() {
if (tblMasterData == null) {
tblMasterData = new JTable() {
private static final long serialVersionUID = 1L;
public TableCellEditor getCellEditor(int row, int column) {
TableColumn tableColumn = getColumnModel()
.getColumn(column);
TableCellEditor editor = tableColumn.getCellEditor();
try {
if (editor == null) {
final JTextField text = new JTextField();
/*
text.addKeyListener(new java.awt.event.KeyAdapter() {
public void keyPressed(KeyEvent e){
}
});
SwingUtilities.invokeLater(new Runnable(){
public void run(){
}
});
*/
editor = new DefaultCellEditor(text);
;
return editor;
}
} catch (Exception e) {
LogWriter.error(e);
}
return editor;
}
};
}
return tblMasterData;
}
Any suggestion?
better could be select all text in the JTable cell
text.setText(text.getText())
text.selectAll
by wrapping into invokeLater()
great workaround Table Select All Editor by #camickr
Ohh! trashgod. Your example saved my life :D
All I needed was the code below and it worked. How easy! Thank you very very much.
private JTable getTblMaster() {
if (tblMasterData == null) {
tblMasterData = new JTable() {
public boolean editCellAt(int row, int column, EventObject e){
boolean result = super.editCellAt(row, column, e);
final Component editor = getEditorComponent();
if (editor == null || !(editor instanceof JTextComponent)) {
return result;
}
if (e instanceof KeyEvent) {
((JTextComponent) editor).selectAll();
}
return result;
} ....
I did it like this. First I am using the event keyReleased and then getting the row and column number on which I am working and then setting the value at that row. Code goes like this.
private void purchases_TBLKeyReleased(java.awt.event.KeyEvent evt) {
int rowWorking = purchases_TBL.getSelectedRow();
int columnWorking = purchases_TBL.getSelectedColumn();
if(columnWorking==3){
model.setValueAt(null, rowWorking, columnWorking);
}
}
This makes the third column of the table null as soon as I focus move on to that using keyboard.
Note: The same piece of code can be placed in MouseClicked event.