Notification when a checkbox in a JTable is checked - java

I've searched for this for quite a while and haven't found a clear example anywhere. I'm a Java newbee using NetBeans. I have a boolean value in the first column of a JTable (called "Enabled") and I have some plugin code that I need to call to see if it has the settings it needs in order to be enabled, and if not, display a message box and prevent Enabled from being checked.
All I really need is for a function to be called when the checkbox is checked and I can take it from there. Does anyone have an example of how to do this?
Thanks for your help!
Harry

You probably want a TableModelListener, as discussed in Listening for Data Changes. Alternatively, you can use a custom editor, as discussed in Concepts: Editors and Renderers and the following section.

All I really need is for a function to
be called when the checkbox is checked
When the checkbox is checked then the value will be changed in the model, which is probably not what your want. I would think you want to prevent the checking of the checkbox in the first place.
The way to prevent a cell from being editable is to override the isCellEditable(...) method of JTable. By overriding this method you can dynamically determine if the cell should be editable or not.
JTable table = new JTable( ... )
{
public boolean isCellEditable(int row, int column)
{
int modelColumn = convertColumnIndexToModel( column );
if (modelColumn == yourBooleanColumn)
return isTheBooleanForThisRowEditable(row);
else
return super.isCellEditable(row, column);
}
};
And a fancier approach would be to create a custom renderer so that the check box looks "disabled" even before the user attempts to click on the cell. See the link provided by trashgod on renderers.

Related

Deselecting the selected row in a JTable

I have created a UI using Swing with a JTable. I have implemented a ListSelectionListener through which I am able to fetch records based on the selected row in the table. I am unable to deselect the row after selection.
So basically I should be able to select a row with one click and then I should be able to deselect the row with another click.
I tried using tableName.getSelectionModel.clearSelection, but I don't know how to see if a row is selected or not. What would tell me this?
Another solution I tried is using a Mouse Click Listener. Again, I am not able to write the condition to check if the mouse click happens on the previously selected row. Is there a way by which I can get the previous row selected?
I am using the DefaultTableModel.
This functionality is supported by default by holding down the "Control" key when you use the mouse click. This is the standard that is used by most applications.
If you really want to use a non standard approach then you should probably be customizing the ListSelectionModel. I would guess you would override the setSelectionInterval(...) method. That is you would first check if the row is currently selected. If so, then invoke the clearSelection() method and return. Otherwise invoke super.setSelectionInterval(...).
It may be old but this works.
table = new JTable() {
public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend)
{
//Always toggle on single selection
super.changeSelection(rowIndex, columnIndex, !extend, extend);
}
};

TableModel in Java: how to specify different renderers for different rows?

I'm making a custom TableModel usingAbstractTableModelfor a project, and I need to figure out a way to have a checkbox show up on some rows, but not others. I have already implemented a getColumn method, but I want to be able to have a checkbox not show up unless a certain condition in another column is reached (for example, if the object represented by a particular row is a lightbulb rather than, say, a toaster, give the user a checkbox to turn the light on or off).
I've tried using getValueAt and passing null or a string instead of a Boolean object in the hopes that maybe Swing wouldn't render the checkbox. And it doesn't, but it also throws a nasty set of ClassCastExceptions for trying to cast a String to a Boolean.
Does anyone have any ideas on how I could do something like this?
Columns in a Swing JTable are displayed using cell renderers. You should read How to Use Tables in the Java tutorials which has a section that describes how the mechanism works. This is how the core method of a custom cell renderer looks like:
public Component getTableCellRendererComponent(JTable table, Object color,
boolean isSelected, boolean hasFocus,
int row, int column) {
The task of this method is to select and prepare a Component (by setting desired colors, fonts, images...) for a specific row and column that the framework will use paint on to its Graphics context. There is a DefaultTableCellRenderer that might do the trick without too much custom code (see the tutorial). Note that this rendering mechanism is an optimization chosen by the Swing developers.
You can also learn a lot about customizing Swing components in Swing Hacks. The examples are not especially well-designed code but just show how to make creative use of the Swing API.
Good luck!
Example (see comments):
final JTable orderTable = new JTable(dataModel);
// All columns with class Boolean are renderered with MyFancyItemRenderer
orderTable.setDefaultRenderer(Boolean.class, new MyFancyItemRenderer());
// Setting the cell renderers explicitly for each column
final TableColumnModel columnModel = orderTable.getColumnModel();
final TableColumn itemCountColumn = columnModel.getColumn(ITEM_COUNT);
itemCountColumn.setCellRenderer(new MyFancyItemRenderer());
// ...
final TableColumn sumColumn = columnModel.getColumn(SUM);
sumColumn.setCellRenderer(new MyFancyPriceRenderer());

How to get JTable selectedRow and selectedColumn during FocusLost event

Whenever there is a lostFocus inside a JTable, i need to capture the existing cell's row and column.
However, the condition below is always false because the source is always either a JTextField or a JComboBox.
public void focusLost(FocusEvent e) {
int row, col;
Object source = e.getSource();
if(((Component) source).getParent() instanceof JTable_Ext){ //<-- always false
table = (JTable_Ext) ((Component) source).getParent();
row = table.getSelectedRow();
col = table.getSelectedColumn();
}
To mitigate the above, i remember the row and col during FocusGained (as class level variable). The problem is, if the user click very fast all over the place within the JTable, somehow the row and column information will be out of sync.
Is there a way to get the Row and Col during FocusLost? if not, is there a better way of doing this?
Well, there is the oppositeComponent. The weird thing is, if this listener is attached to the table, the documentation tells that the table should be the "source" component (because it is a FocusLost event and the component that lost the focus is the table itself).
http://download.oracle.com/javase/1.4.2/docs/api/java/awt/event/FocusEvent.html#getOppositeComponent%28%29
Could you just keep a record of row & column using
table.getSelectionModel().addListSelectionListener(...);
table.getColumnModel().getSelectionModel().addListSelectionListener(...);
So every time the use clicks update it, not just on focus events?
If you just want to save the data that was entered in the cell (without hitting return), then you don't need to do anything. The updated information is contained within TableModel of the JTable.
Otherwise you can take a look at .tableChanged() and the associated TableModelEvent, which gives you the last row/column modified. You could keep a variable that is always updated to the latest event row/column. I guess that if you change a cell number without hitting return, it nonetheless registers as an event.
However, the condition below is always false because the source is always either a JTextField or a JComboBox.
This implies that the focusLost event is being generated when you begin editing a cell. So the question is why are you doing this? I think you need to state your actual requirement, because you attempted solution does not seem appropriate.

refresh setCellEditor for JTable

I was looking for an answer for a previous question and had an ingenious idea to overcome a limit on JTable. I need the editor to be different on a row by row basis, whereas JTable can only handle a single editor for each column.
So my idea is to use a MouseListener to check the row and column on the JTable and set new editor each time.
But, calling setCellEditor() a second time do not have any effect. The editor remains to be the first one that was set. So how can I make "setCellEditor" work a second time for the same column?
Here's the code in MouseListener.
public void mouseClicked(MouseEvent e) {
int cols = resultTable.columnAtPoint(new Point(e.getX(), e.getY()));
int rows = resultTable.rowAtPoint(new Point(e.getX(), e.getY()));
StorageObject item = (StorageObject) resultTable.getModel().getValueAt(rows, cols);
TableColumn col = resultTable.getColumnModel().getColumn(cols);
col.setCellEditor(new MyComboBoxEditor(item.list));
}
I'm not sure why your code isn't working (it's been a while since I've done Swing), but why don't you just override
public TableCellEditor getCellEditor(int row, int column)
On your JTable? Maintain a map of the combo boxes you want to use for each row and in your overriden method return the correct one.
My theory is that when all the mouse listeners registered to the Table/TableCell are invoked, the ones installed to the API classes by default will be invoked first, before your mouse listener. This means the event causing the editor to be fetched will occur before you set it to a different one. Kind of like a race condition, only it's actually defined somewhere in the API source code... That's my naive theory and I can already see some holes in it, so on to my solution:
Override JTable.getCellEditor(int row, int col). This allows you to return whatever editor you want for any cell.

Editable JTable Tutorial

Are there any good books or website that go over creating a JTable? I want to make one column editable. I would like to actually put a inherited JCheckBox component (that we created here) into one of the table columns instead of just having the table put JCheckBox in based on it being an editable boolean field.
I have the JFC Swing Tutorial Second Edition book but I just would like to know if there are other examples I could look at and learn how to deal with the tables better. The book seems to just take the java 'trail' online and put it in the book.
I am re-reading the stuff though, just curious if anyone has found something that might help out more.
To make a column editable you have to override the isCellEditable method in the TableModel. Creating a TableModel is fairly easy if you inherit AbstractTableModel and I'd recommend it for all but the most simple JTables.
However, adapting the TableModel is only part of what you need to do. To actually get a custom component in the JTable, you need to set a custom cell renderer. To use an interactive custom component, you need to set a custom cell editor. In some cases, it's enough to use slightly modificated versions of the default classes for this.
Editors
If you already have got a custom component is easily done using delegation: Create a new class implementing TableCellEditor, and return a new instance of the component in the getCellEditorComponent method. The paramaters to this method include the current value as well as the cell coordinates, a link back to the table and wether or not the cell is selected.
The TableCellEditor also has a method that is called when the user commits a change to the cell contents (where you can validate user input and adjust the model) or cancels an edit. Be sure to call the stopEditing() method on your editor if you ever programmatically abort editing, otherwise the editor component will remain on screen -- this once took me like 2 hours to debug.
Note that within a JTable editors and only editors receive events! Displaying a button can be done using a renderer. But to get a functioning button, you need to implement an editor with the correct EventListeners registered. Registering a listener on a renderer does nothing.
Renderers
Implementing a renderer is not strictly necessary for what you describe in your question, but you typically end up doing it anyway, if only for minor modifications. Renderers, unlike editors, are speed critical. The getTableCellRendererComponent of a renderer is called once for every cell in the table! The component returned by a renderer is only used to paint the cell, not for interaction, and thus can be "reused" for the next cell. In other words, you should adjust the component (e.g. using setText(...) or setFont(...) if it is a TextComponent) in the renderer, you should not instantiate a new one -- that's an easy way to cripple the performance.
Caveats
Note that for renderers and editors to work, you need to tell the JTable when to use a certain renderer/editor. There are basically two ways to do this. You can set the default cell renderer/editor for a certain type using the respective JTable methods. For this way to work, your TableModel needs to return exactly this type in the getColumnClass(...) method! The default table model will not do this for you, it always returns Object.class. I'm sure that one has stumped a lot of people.
The other way to set the editor/renderer is by explicitly setting it on the column itself, that is, by getting the TableColumn via the getTableColumn(...) method of the JTable. This is a lot more elaborate, however, it's also the only way to have two different renderers/editors for a single class. E.g. your model might have two columns of class String which are rendered in entirely different ways, maybe once using a JLabel/DefaultRenderer and the other using a JButton to access a more elaborate editor.
JTable with its custom renderers and editors is extremely versatile, but it is also a lot to take in, and there are a lot of things to do wrong. Good luck!
How to Use Tables in The Swing Tutorial is mandatory reading for anyone customising JTables. In particular, read and reread Concepts: Editors and Renderers because it typically takes a while for it to "click". The examples on custom renderers and editors are also very worthwhile.
The class you want to look into extending to create your own behavior is DefaultTableModel. That will allow you to define your own behavior. A decent tutorial can be found on sun's site.
This tutorial from the java lobby is easy to follow. The online Swing trail for JTable that you reference indicates that it has been updated. Did you scan through the whole thing for possible better (isn't newer always better) information?
If you are trying to use a simple JTable with 1 column editable and you know the column location you could always use default table model and overload the isCellEditable call.
something like this :
myTable.setModel(new DefaultTableModel(){
#Override
public boolean isCellEditable(int row, int column) {
if (column == x) {
return true;
} else
return false;
}
});
And for the check box create a renderer class
MyCheckBoxRenderer extends JCheckBox implements TableCellRenderer
Some useful classes are:
Package javax.swing.table :
TableModel - Interface for a tablemodel
AbstractTableModel - Nice class to extend for creating your own table with custom data structures
DefaultTableModel - Default table model which can deal with arrays[] and Vectors
To disable editing on any cell you need to override the isCellEditable(int row, int col) method
in your table Model, you should override "isCellEditable" and "setValueAt" functions, like below.
Column 4 is the column for editable cells.
Then when you double click the cell and type something,
setValueAt() will be called and save the value to the tableModel's DO,field col4.
public ArrayList<XXXDO> tbmData = new ArrayList<XXXDO>(); //arraylist for data in table
#Override
public boolean isCellEditable(int row, int col) {
if (col == 4) {
return true;
} else {
return false;
}
}
#Override
public void setValueAt(Object value, int row, int col) {
if ((row >= 0) && (row < this.tbmData.size()) && (col >= 0) && (col < this.colNm.length)) {
if (col == 4) {
tbmData.get(row).col4= (String) value;
}
fireTableCellUpdated(row, col);
} else {
}
}

Categories