File APIs for Java Developers
Manipulate DOC, XLS, PPT, PDF and many others from your application.
http://aspose.com/file-tools
The moose likes Swing / AWT / SWT and the fly likes Wanted: conceptual tutorial on JTable Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Swing / AWT / SWT
Bookmark "Wanted: conceptual tutorial on JTable" Watch "Wanted: conceptual tutorial on JTable" New topic
Author

Wanted: conceptual tutorial on JTable

Ralph Cook
Ranch Hand

Joined: May 29, 2005
Posts: 479
I have been through 6 or 8 tutorials on JTable now, and none of them have been nearly complete. I understand it's a large topic, and that few people will WANT a complete one, but I do.

Further, I want something that will explain the concepts behind the major classes, instead of ONLY saying "Here's an example -- now you know how to do this." An example will show you how to do one specific thing, and, if you're lucky, you can make an educated guess at how to do the thing YOU want to do based on that. But it's pretty hit-or-miss, depending on the example being similar enough to what you want to do AND the example not obscuring some concept that is important to you but not to the example.

One example of the sort of thing I'd like to know: you can implement a TableModel for your JTable by either using or extending DefaultTableModel, or extending AbstractTableModel. There is also an interface TableModel, but there is a curiosity about it: it has methods for adding and removing TableModelListener objects, but none for firing events or retrieving the list of listeners. AbstractTableModel has fireTableDataChanged(), fireTableStructureChanged, fireTableRowsInserted, etc. -- who's calling these methods, and on what? Whoever it is can't be accepting something that just implements TableModel, because it doesn't have those methods.

Does that mean that you cannot implement your own TableModel class, and therefore MUST use one of the other two, extended if necessary?

I hope this illustrates my need for a conceptual explanation of JTable and TableModel interactions. Although it is hard to believe my use is unique, I have yet to find an example that covers it. So does anyone know where I can find a conceptual explanation of JTable, in depth?

rc
Stephan van Hulst
Bartender

Joined: Sep 20, 2010
Posts: 3371
    
    9
I don't know any tutorials, but I might be able to answer some questions.

JTable uses the MVC pattern. Here, TableModel is the model, it stores the data and provides methods to read and update data. JTable uses TableCellRenderer for the view, and TableCellEditor for the controller parts.
Now, you like to be able to change the data through components other than the JTable. When you do this, you will still want to display the new data in your table as soon as it is updated. This is why TableModel provides a method addTableModelListener(). JTable adds a listener to your model, to determine when an update happens. You can also add your own listeners, if you have other components that display elements from your model.

The TableModel interface provides all the definitions needed for the table to read and update data, and get notified of changes. So now all we have to do is implement it.
Do you really want to implement the addTableModelListener() each time you have a new model? Thankfully, AbstractTableModel already implements it, and you only have to focus on what data your model returns.
The only methods AbstractTableModel doesn't implement are:
Usually, you would also override the following methods though:
Okay, now there's one problem left. If the data underlying the TableModel changes, how will the AbstractTableModel know that it has to notify its listeners? This is where the fire... methods come in. Your underlying class calls these methods to tell the TableModel: "Hey, it's time to warn the Table that our data has changed".

As you can see, this is an implementation detail, and therefore these methods are not part of the TableModel API. They should probably have made these methods protected.

DefaultTableModel is the default model used by JTable. You should almost always extend AbstractTableModel instead.

I will see if I can make an example (yes sorry, one example) showing you how the fire methods are used.
Ralph Cook
Ranch Hand

Joined: May 29, 2005
Posts: 479
Stephan van Hulst wrote:I don't know any tutorials, but I might be able to answer some questions.

We'll make our own tutorial if we have to. I appreciate your efforts; this is a good start.

Stephan van Hulst wrote:
JTable uses the MVC pattern. Here, TableModel is the model, it stores the data and provides methods to read and update data. JTable uses TableCellRenderer for the view, and TableCellEditor for the controller parts.
Now, you like to be able to change the data through components other than the JTable. When you do this, you will still want to display the new data in your table as soon as it is updated. This is why TableModel provides a method addTableModelListener(). JTable adds a listener to your model, to determine when an update happens. You can also add your own listeners, if you have other components that display elements from your model.

The TableModel interface provides all the definitions needed for the table to read and update data, and get notified of changes. So now all we have to do is implement it.

Actually, TableModel only has methods to add and remove a listener. No 'fire...' methods are defined in TableModel.
Stephan van Hulst wrote:
Do you really want to implement the addTableModelListener() each time you have a new model? Thankfully, AbstractTableModel already implements it, and you only have to focus on what data your model returns.
The only methods AbstractTableModel doesn't implement are:
Usually, you would also override the following methods though:
Okay, now there's one problem left. If the data underlying the TableModel changes, how will the AbstractTableModel know that it has to notify its listeners? This is where the fire... methods come in. Your underlying class calls these methods to tell the TableModel: "Hey, it's time to warn the Table that our data has changed".

As you can see, this is an implementation detail, and therefore these methods are not part of the TableModel API. They should probably have made these methods protected.

DefaultTableModel is the default model used by JTable. You should almost always extend AbstractTableModel instead.

I will see if I can make an example (yes sorry, one example) showing you how the fire methods are used.

Practically every time I say I want an explanation instead of only an example, the reader thinks I object to examples. This is not the case. Examples are fine, it's just that they don't explain how things work, they just show how things work in one instance. The more complicated the interface, the more important it is to understand how things work in general, otherwise you must remember a hopeless number of details and rules without a conceptual structure to help make sense of them.

Can we take this another step? My current specific problem involves having columns that the user can cause to be displayed or not; therefore I must write my model to store information about columns that are not displayed. I do not want to set the width of a column to zero to hide it, since such a column can still get focus.

It appears that AbstractTableModel.getRowCount() and .getColumnCount() should return visible columns and rows; the javadoc for TableModel is misleading, or assumes that there are never columns that are not displayed.

I gather, through reading a lot of forum postings here and elsewhere, that TableColumnModel also represents the visible columns. I've confirmed this by plowing through some JTable source; it calls TableColumnModel.getColumn() with a index from the view.

So now I'm trying to get an understanding of how to implement a set of columns, where each one may or may not be displayed at a given time. This is harder than I would have imagined. My current guess is to extend DefaultTableColumnModel (again, it has fire... methods and the interface it implements does not), and keep a separate list of actual columns and let it keep its list of visible columns, and frantically translate back and forth between the two as the user chooses columns to display or hide. Does this look like the right approach? Do you have other suggestions? Are there other traps to look out for?

rc
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19543
    
  16

TableModel has a representation of the different columns that can be displayed. By default, a JTable creates a TableColumnModel that uses one column for each column in the model (in JTable.createDefaultColumnsFromModel()). The latter is used to determine what columns are actually visible. You can easily create an empty DefaultTableColumnModel, then add columns, then use that to create a JTable with only the columns you need.

In your case there should be no need whatsoever to change the TableModel's columns. Once set they should remain set. It's the TableColumnModel (with DefaultTableColumnModel as its only known implementation) that you should use to hide (remove) and show (add) columns. You can move columns from one position to another if needed. You don't need to worry about calling the fire methods; that's done automatically when you add, remove or move columns.

As for the TableColumn instances you need, they only require an index of the model's column. The following little example shows you how to create a JTable with all evenly-numbered columns in reversed order:
Because this allows a table's column's position to not match the model's column's position JTable has methods for converting between the two: convertColumnIndexToView to convert from the model's index to the first index of the matching column in the JTable, and convertColumnIndexToModel to convert from the table's index to the model's index. (Because of the ability to filter out rows graphically using RowFilter there are now also methods to do the same for row numbers.)

As for your specific situation. Whether or not a column is visible or not should not be part of the model. If you do this then hiding the column in one table will hide it in all other tables using the same model as well. So that leaves the JTable itself or, preferably, the TableColumnModel. A quick attempt:
This moving code has one problem though; it assumes that all visible columns are in order. Because by default a user can drag a JTable's columns this may not be the case. Use table.getTableHeader().setReorderingAllowed(false) to prevent this.

One addition you could make is add a TableModelListener to the TableModel to be notified when a column is added or removed. However, this usually means recreating the entire TableColumnModel as there is no specific event for just adding / removing a column.

And a little warning: don't let anyone else call addColumn / removeColumn, or columns could be shown multiple times (calling showColumn, then addColumn with the same index) or not at all (calling removeColumn, then showColumn, since removeColumn does not modify viisble correctly). Perhaps you can override addColumn and removeColumn to prevent this:
But if you do this, make sure to call super.addColumn, super.removeColumn and super.moveColumn in the methods of my example; this will cause them to use the implementation of DefaultTableColumnModel instead of the just overridden methods.

Wow, quite a long post in the end. And I'm sure Rob C will find some error in it as well

And perhaps it's not that hard to create a completely new TableColumnModelListener implementation, although the firing of events would then need to be done manually.


SCJP 1.4 - SCJP 6 - SCWCD 5 - OCEEJBD 6
How To Ask Questions How To Answer Questions
Ralph Cook
Ranch Hand

Joined: May 29, 2005
Posts: 479
Rob Spoor wrote:TableModel has a representation of the different columns that can be displayed. By default, a JTable creates a TableColumnModel that uses one column for each column in the model (in JTable.createDefaultColumnsFromModel()). The latter is used to determine what columns are actually visible. You can easily create an empty DefaultTableColumnModel, then add columns, then use that to create a JTable with only the columns you need.

In your case there should be no need whatsoever to change the TableModel's columns. Once set they should remain set. It's the TableColumnModel (with DefaultTableColumnModel as its only known implementation) that you should use to hide (remove) and show (add) columns. You can move columns from one position to another if needed. You don't need to worry about calling the fire methods; that's done automatically when you add, remove or move columns.

As for the TableColumn instances you need, they only require an index of the model's column. The following little example shows you how to create a JTable with all evenly-numbered columns in reversed order:


With your explanation (AND your example 8>), I got something that I had not yet understood about how all this works -- the TableModel.getColumnCount() returns the count of all columns, not just visible columns. This is what I mean about getting an explanation of concepts; one can read a lot of examples and not understand that principle.
Rob Spoor wrote:
Because this allows a table's column's position to not match the model's column's position JTable has methods for converting between the two: convertColumnIndexToView to convert from the model's index to the first index of the matching column in the JTable, and convertColumnIndexToModel to convert from the table's index to the model's index.

Out of curiousity, I looked up the JTable source for doing this conversion. It does

thereby using the model index stored in the TableColumn object.
Rob Spoor wrote:

As for your specific situation. Whether or not a column is visible or not should not be part of the model. If you do this then hiding the column in one table will hide it in all other tables using the same model as well.

A good point, as long as that isn't the behavior I'm after.
Rob Spoor wrote:So that leaves the JTable itself or, preferably, the TableColumnModel. <'quick attempt' code snipped>

Why do you prefer the TableColumnModel? It was the first thing I thought of too, but it is defined to deal with visible columns only, and many of its methods are going to be confusing to the reader for a class that holds both visible and not-visible columns. addColumn(), getColumn(), getColumnCount(), etc., are all defined by Java's TableColumnModel, and if I extend the class so that "column" could mean one that was visible or not, it's just hard to keep track of what you're reading.

I can create my own class for holding "all possible columns", of course, and am somewhat inclined to do that and have my TableModel contain it separately from the 'VisibleTableColumnModel". Or maybe it's just a collection of column objects in TableModel. But I'd be interested in any other reasons you have for designing it your way.
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19543
    
  16

Ralph Cook wrote:
Rob Spoor wrote:So that leaves the JTable itself or, preferably, the TableColumnModel. <'quick attempt' code snipped>

Why do you prefer the TableColumnModel? It was the first thing I thought of too, but it is defined to deal with visible columns only, and many of its methods are going to be confusing to the reader for a class that holds both visible and not-visible columns. addColumn(), getColumn(), getColumnCount(), etc., are all defined by Java's TableColumnModel, and if I extend the class so that "column" could mean one that was visible or not, it's just hard to keep track of what you're reading.

That's the separation between model and view. The model (TableModel) is there only to store and manipulate the data. The view (JTable plus JTableHeader plus TableColumnModel plus ListSelectionModel) are for the graphical representation of that data. In your case the data doesn't (or at least shouldn't) change, only the view. So that's why I wouldn't handle the showing / hiding in the TableModel but the TableColumnModel. And if you want to show / hide the columns in all tables that's still possible, by letting them share the same TableColumnModel.

I can create my own class for holding "all possible columns", of course, and am somewhat inclined to do that and have my TableModel contain it separately from the 'VisibleTableColumnModel". Or maybe it's just a collection of column objects in TableModel. But I'd be interested in any other reasons you have for designing it your way.

The class for holding all possible columns should be the TableModel. As such, the TableModel shouldn't contain that class, it should be that class.

But if DefaultTableColumnModel or my quickly written class aren't good, TableColumnModel is still an interface that can be implemented in many, many ways. Building one directly on top of your TableModel is a valid possibility. It's not quite possible to have one class implement both interfaces though, as both interfaces have method getColumnCount() that means one thing (available columns) in one interface and another thing (visible columns) in the other.
Ralph Cook
Ranch Hand

Joined: May 29, 2005
Posts: 479
Ralph Cook wrote:
Why do you prefer the TableColumnModel? It was the first thing I thought of too, but it is defined to deal with visible columns only, and many of its methods are going to be confusing to the reader for a class that holds both visible and not-visible columns. addColumn(), getColumn(), getColumnCount(), etc., are all defined by Java's TableColumnModel, and if I extend the class so that "column" could mean one that was visible or not, it's just hard to keep track of what you're reading.
Rob Spoor wrote:
That's the separation between model and view. The model (TableModel) is there only to store and manipulate the data. The view (JTable plus JTableHeader plus TableColumnModel plus ListSelectionModel) are for the graphical representation of that data. In your case the data doesn't (or at least shouldn't) change, only the view. So that's why I wouldn't handle the showing / hiding in the TableModel but the TableColumnModel. And if you want to show / hide the columns in all tables that's still possible, by letting them share the same TableColumnModel.

The class for holding all possible columns should be the TableModel. As such, the TableModel shouldn't contain that class, it should be that class.


Representing the columns to be displayed is not entirely in the domain of the model. A number of fields and methods involved are concerned with display, such as default width, rendering classes, default order, default display etc. Yes, they also have to do with data in the model, I'm not saying they're entirely OUT of that domain either. Even if they were entirely in that domain, I would feel free to create a class just for representing them, and have it contained by or referred to by TableModel. There is nothing that says that any part of an MVC design all has to reside in one class, thank goodness.

I don't think TableColumnModel is a good choice for representing all possible columns. It is already defined by the Swing classes to represent only visible columns, and the names of public methods, etc., just refer to them as "columns". If I extend that to represent all possible columns, the code in it will be confusing as to whether each column reference is one or the other, and I can't control the external names. I'm not saying it won't work, only that it seems a bit cleaner to leave that class definition alone and make another class with all possible columns.

I thank you both (Rob and Stephen) for all your help -- I feel like I have a much better handle on this part of my problem now.
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19543
    
  16

Ralph Cook wrote:Representing the columns to be displayed is not entirely in the domain of the model. A number of fields and methods involved are concerned with display, such as default width, rendering classes, default order, default display etc.

Ah, but you said the magical word: "displayed". That makes it the responsibility of the view. TableColumn has methods for the width and rendering / editing. Default display (I assume you mean if a column should be shown or not) is another part that should go in the view, in this case your own TableColumnModel. The default order could go into both the TableColumn and the TableColumnModel.

Yes, they also have to do with data in the model, I'm not saying they're entirely OUT of that domain either. Even if they were entirely in that domain, I would feel free to create a class just for representing them, and have it contained by or referred to by TableModel. There is nothing that says that any part of an MVC design all has to reside in one class, thank goodness.

While you can do that, it goes against the MVC pattern. The model shouldn't care at all about how its data is displayed.

I don't think TableColumnModel is a good choice for representing all possible columns. It is already defined by the Swing classes to represent only visible columns, and the names of public methods, etc., just refer to them as "columns". If I extend that to represent all possible columns, the code in it will be confusing as to whether each column reference is one or the other, and I can't control the external names. I'm not saying it won't work, only that it seems a bit cleaner to leave that class definition alone and make another class with all possible columns.

I agree, and so does Sun. That's why there are the two model classes for JTables: one for all possible columns (TableModel) and one for the visible columns (TableColumnModel).

Like I said, I'd go for two classes: a TableModel implementation that contains all data you have available, so all available columns, and a TableColumnModel implementation that deals with the "default width, rendering classes, default order, default display etc". It uses the TableModel to get its data, but it creates the TableColumn objects, gives it their preferred widths, renderers, editors, adds them in the order you want etc. That's also the class you use for showing / hiding columns. Just because it implements TableColumnModel doesn't mean you can't give it any other methods to control the columns.
Ralph Cook
Ranch Hand

Joined: May 29, 2005
Posts: 479
I think the terminology is beginning to defeat us. You are contradicting yourself, and since I'm sure you don't mean to, I suspect you don't understand what I'm saying as well as you think you do, and certainly not well enough to help me design for it.

I understand MVC. Talking down to me - or anyone - rarely increases understanding.

As I said, I do thank you for being a big help on understanding these JTable classes.

rc
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19543
    
  16

Ralph Cook wrote:I think the terminology is beginning to defeat us. You are contradicting yourself, and since I'm sure you don't mean to, I suspect you don't understand what I'm saying as well as you think you do, and certainly not well enough to help me design for it.

I understand MVC. Talking down to me - or anyone - rarely increases understanding.

I wasn't trying to talk down to you, trust me. I think you're right and I just got your requirements wrong, and therefore I was only trying to correct you.
Rob Camick
Ranch Hand

Joined: Jun 13, 2009
Posts: 2091
    
    7
Talking down to me - or anyone - rarely increases understanding.


I find comments like that completely unproductive. I don't see any spot where Rob is "talking down to you". In fact Rob has spent much time trying to reply in detail to your concerns and should be applauded for going above and beyond in terms of the time spent in answering this question.

If this is how you treat people who generously spend time trying to help you it sure makes me less likely to help you in the future.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Wanted: conceptual tutorial on JTable
 
Similar Threads
JTable
JTable with JList
JTable
It must present search results in a JTable.
JTable