I have a question about my application's design.
I have 2 JTables (with my own TableModel) and I want to create a third table, which is built based on the selection in the other two tables. So when I start my application nothing is selected and this third table is supposed to be empty.
How do I find out what is selected in the two tables and how do I refresh the third one?
How do I find out what is selected in the two tables and how do I refresh the third one?
Please note you have two different yet related problems here.
First one is about selection in tables (see User Selections section of How to Use Tables tutorial). Basically you need to attach a ListSelectionListener to the table's ListSelectionModel in order to listen for selection changes. Something like this:
JTable table1 = new JTable();
table1.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
// Implementation here
}
});
See JTable API:
public ListSelectionModel getSelectionModel()
public int[] getSelectedRows()
public int getSelectedRowCount()
public int convertRowIndexToModel()
The second problem is about refreshing/updating your third table. In order to accomplish this you need to work with the model of this third table by adding/removing/updating rows. Then this model will notify the view that something has changed and your table will be automatically updated.
Related
I'm starting to learn how implement MVC pattern in swing. I have a confusion when in comes to the data in model.
In the application that I'm doing, I have several JTextFields. The data set in the fields come from JTable. I have a ListSelectionListener in my controller added to that jtable (in the view) so that when selection has changed, the data from the selected row will be reflected to the respective textfields
public void transferTableDataToFields(){
if(tblProduct.getSelectedRows().length != 0){
int selRow = tblProduct.getSelectedRow();
txtID.setText(tblProduct.getValueAt(selRow, 0).toString());
txtName.setText(tblProduct.getValueAt(selRow, 1).toString());
txtDescIn.setText(tblProduct.getValueAt(selRow, 2).toString());
txtSupplier.setText(tblProduct.getValueAt(selRow, 4).toString());
txtPrice.setText(tblProduct.getValueAt(selRow, 5).toString());
}
}
My question is, am I doing it the right way? Should I define fields in the model that corresponds to the textfields in my view, then change my code to controller setting the model's fields to values from view's jtable then let the model fire a property change notification to its listener, then let the listener call the update method in the view based on the property change event?
My problem with this approach is that, the view should have a method that will return the row selected from the jtable which I find a bit ugly.
EDIT
#trashgod this is what I'm saying.
Inside my TableModel implementation, is it better to populate the data here?
public MyTableModel extends AbstractTableModel{
List<Row> data;
Row header;
public MyTableModel(){
initializeData();
}
public void initializeData{
//query database here then put it in the list
}
//other methods to implement e.g. getvalueAt(int x, inty){data.get(x).get(y);}
}
Is it better if I make it like this? because currently I stored and populate the data of my table in my main model (also containing fields with corresponding textfields in the SelectedRow view) then I pass the data in my JTable's model.
In this case, the current selection is a property of the view, an instance of JTable, not the model, an implementation of TableModel.
If you choose to supersede editing in the JTable itself, a ListSelectionListener is the correct way to update your (anonymous) dependent view, e.g. SelectedRow. Your controller should have little to do except add SelectedRow as a listener to your JTable. As SelectedRow contains (presumably) editable fields, you are responsible for several things:
Propagate any changes back to the original table's TableModel, typically via setValueAt().
Convert between view and model coordinates, mentioned here.
Preclude (or synchronize via the ListSelectionListener) editing in the source JTable.
Addendum: Inside my TableModel implementation, is it better to populate the data here?
The answer depends on the application. Your TableModel implementation should expose a public API that provides as much (or as little) as required to support the application's requirements for concurrency and latency. For reference, DefaultTableModel is a general purpose example, although you'll want to use something more recent than Vector internally.
I have a column in JTable that binds to the underlying boolean property on a list of business objects. I also have a combobox, which should select which items should be selected. I basically added the following code as a handler to the combobox:
macroCombo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JComboBox comboBox = (JComboBox) e.getSource();
Predicate filter = (Predicate) comboBox.getSelectedItem();
for(SelectableKey key : tableEntries){
key.setSelected(filter.evaluate(key));
}
}
});
I also have a few other controls I want to change based on the value. At the moment, only a few cells in the table change their state to be selected/deselected. Only when I click on the row, or select multiple rows, the UI updates itself. Is there a call from the handler I need to make to tell GUI to redraw itself? ALos, if I modify other controls than JTable, how would I tell them to change their state?
Thanks
When you update a value in your TableModel, the model should fire a corresponding TableModelEvent (type: UPDATE).
If your TableModel for example extends from AbstractTableModel, you can call the fireTableRowsUpdated method after you have made the changes.
Another approach is a TableModel which knows when it gets updated (for example by adding listeners to the objects it contains). This allows other code to simply update the objects contained in the TableModel, without having knowledge of the TableModel. The TableModel itself will then fire the event when it detects changes made to the objects it contains.
I prefer the second approach, as this avoids that I have to pass that TableModel around to all my other classes.
Consult the table tutorial for more information.
My hobby-level programing won't let me extend my program the way I want to, and my books/google won't help me out too much, hopefully you guys can!
I'm writing a simple database viewing program using the 'ResultSetTableModel' to import data from my local mySQL database.
The current program displays the entries in a nice list, and all of the data contained in each entry is presented as an individual cell in the JTable.
However, I now want to implement a feature to the JTable rows, that 'on click' displays the content (in my case an image using an image path)
However, after trying endlessly I cannot seem to find an easy and short way to add an eventhandler that does this for me.
I've managed to do similar things with manually populated tables before, but when trying to apply the lessons learned there to this new project gets me nowhere.
You can simply add a ListSelectionListener to the the table selectionmodel:
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
// Last selected row:
table.getSelectedRow(); // -1 if no row selected
// All selected rows:
table.getSelectedRows();
}
});
Have you tried adding a MouseListener (or MouseAdapter) to the the table - e.g.
table.addMouseListener(new MouseAdapter(){
#Override
public void mouseClicked(MouseEvent e){
// do something cool...
}
}
I have been stumped with this for quite some time now. I understand you use the table model to refresh the actual table with the new values however I cannot seem to get this to work. I have added a tablemodellistener to my form and have a tableChanged method. However, I cannot seem to figure out why the tableChanged method isn't getting called when I insert into a the table.
public void tableChanged(TableModelEvent e) {
int row = e.getFirstRow();
int column = e.getColumn();
DefaultTableModel model = (DefaultTableModel)e.getSource();
// String columnName = model.getColumnName(column);
//Object data_1 = model.getValueAt(row, column);
//model.fireTableCellUpdated(row, column);
//model.fireTableDataChanged();
//customerTable.repaint();
}
Could I completely rebuild the table if I click the refresh button on my form? Would that at all be possible? If not, do I have to call my tableChanged method from my refresh button's action performed method in order for it to trigger? I've been stuck on this for quite some time now and I would just like to get this figured out for the benefit of learning.
If you have the option and it fits your needs, I'd reccomend taking a look at GlazedLists. Then you won't have to worry about updating your table models--it's all handled for you.
Here's a jump to a relevant part of the GlazedLists tutorial.
I believe you need to manually add your Table as Listener of table TableModel.
I've implemented my own event handler and added it to the selection model of the table:
table.getSelectionModel().addListSelectionListener(event);
And implemented the method for "event" (mentioned above):
public void valueChanged(ListSelectionEvent e) {
log.debug("value changed");
}
Unfortunately the event fires twice if I chance the selection and it doesn't seem possible to find the associated table, because e.getSource provides javax.swing.DefaultListSelectionModel.
Hence my questions are:
1) Why does it fire twice although the eventListener is only registered once?
2) How can I find the table for which the selection applies? The DefaultListSelectionModel doesn't seem to offer any getSource() or similar.
Many thanks!
Thanks Draemon..It Works fine....
Our Code
vMachinesTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent lse) {
if (!lse.getValueIsAdjusting()) {
System.out.println("Selection Changed");
}
}
});
Thanks By
TF Team
1) I think you'll find it fires once for de-selecting the old selection and once for selecting the new selection. If you log the details of the event you should see exactly what's going on. I can't remember the details, so perhaps this is wrong. Either way you should be able to call getValueIsAdjusting() on the event and only use the last one in the chain (ie when it returns false).
2) You shouldn't normally need to, but AFAIK the only way to do this is to create your Listener specifically for the table (ie pass the table to the constructor and remember it).
Since more than one JTable (or other component I'm guessing) can share the same selection model, it doesn't make sense to ask for the associated JTable from the event. This is the same reason that you can't retrieve a JTable from a TableModel. As Draemon suggests, store the reference to the JTable in (or make it accessible to) your listener class.