How do CellEditors work? - java

I have problems in understanding how CellEditors work in Java.
I have a JTable with a model (extends AbstractTableModel).
The JTable has its CellRenderer and CellEditor.
The CellEditor only overrides
isCellEditable()
(one condition added).
How do the changes I made in one Cell go to the Model?
Does the Model has to implement an CellEditorListener and react on
stopEditing()?
I have read, that the changes would automatically be stored in the model.
Is that true? If yes, how does it work? Do I have to react on
tableChanged()
then?
Please explain the way of the data, which have changed and at which steps I have to do something.
Thanks a lot!

The changes are applied to the model via the TableModel's .setValueAt() method. The JTable itself takes care of receiving the value returned by the CellEditor and passing it to the TableModel.

Related

Difficulties understanding the renderers mechanism of swing's JTable and JTree

Often when using JTable or JTree, user define its own cell renderer.
It is very common to inherit user's component from DefaultTableCellRenderer, and implements the renderer method getTableCellRendererComponent. It turns out that DefaultTableCellRenderer in fact inherits from JLabel, thus returns itself (this) when called to super (at the render method) and thus user's renderer can similarly returns itself (this) as well.
And it all works well.
My question is how can it be?
Each time this method is called by the table, it is given different parameters, and the output label is changed as function of these parameters. If it is indeed the same instance of the label – shouldn't it be changed according to the last call to this method?
Wouldn't it mean that all of the table's cells are infect composed of the same label instance, which holds the same value (value of last call to the renderer method)?
I have searched the web, and dig within Swing's code, and could not find any act of clone or copy constructor that actually duplicates the output label.
I could not find any evidence that (perhaps) swing uses reflection in order to re-instantiate the renderer each time from scratch.
I have read the Swing's tutorial on JTables, and there I could find the next lines:
You might expect each cell in a table to be a component. However, for performance reasons, Swing tables are implemented differently.
Instead, a single cell renderer is generally used to draw all of the cells that contain the same type of data. You can think of the renderer as a configurable ink stamp that the table uses to stamp appropriately formatted data onto each cell. When the user starts to edit a cell's data, a cell editor takes over the cell, controlling the cell's editing behavior.
They give a hint, that indeed what I am saying is correct, but do not explain how its actually being accomplished.
I can't get it. Can any of you?
It's an implementation of the flyweight pattern.
When the JTable repaints itself, it starts a loop and iterates over every cell that must be painted.
For each cell, it invokes the renderer with the arguments corresponding to the cell. The renderer returns a component. This component is painted in the rectangle corresponding to the current table cell.
Then the renderer is called for the next cell, and the returned component (which has a different text and color, for example), is painted in the rectangle corresponding to the cell, etc.
Imagine that each time the renderer is called, a screenshot of the returned component is taken and pasted into the table cell.
In adition to #JB's clear explication of how JTable and JTree use the flyweight pattern, note how both classes provide public methods getCellRenderer() and getCellEditor(). Examine these methods to see how JTable uses Class Literals as Runtime-Type Tokens to select a renderer or editor by class, if none is specified by column. Internally, JTable uses a Hashtable defaultRenderersByColumnClass for instance storage.
After some digging, found the next implementation note from DefaultTableCellRenderer documentation:
Implementation Note: This class inherits from JLabel, a standard component class. However JTable employs a unique mechanism for rendering its cells and therefore requires some slightly modified behavior from its cell renderer. The table class defines a single cell renderer and uses it as a as a rubber-stamp for rendering all cells in the table; it renders the first cell, changes the contents of that cell renderer, shifts the origin to the new location, re-draws it, and so on. The standard JLabel component was not designed to be used this way and we want to avoid triggering a revalidate each time the cell is drawn. This would greatly decrease performance because the revalidate message would be passed up the hierarchy of the container to determine whether any other components would be affected. As the renderer is only parented for the lifetime of a painting operation we similarly want to avoid the overhead associated with walking the hierarchy for painting operations. So this class overrides the validate, invalidate, revalidate, repaint, and firePropertyChange methods to be no-ops and override the isOpaque method solely to improve performance. If you write your own renderer, please keep this performance consideration in mind.
This is essentially what JB explained above.
Thanks for the (quick) answers

Swing JTable custom rendering

I have this kind of progamming task without JavaFx, instead it's Java Swing. I realized my knowledge is still limited.
I have one single JTable.
But, within this JTable I need a custome Cell Renderer.
The goal is to make this kind of JTable: Example image
My current solutions are: Example Image
Create a Single JTable:
get each Column and set its CellRenderer with a custom Renderer (below).
Create a new Class implements TableCellRenderer:
return different JPanel inside getTableCellRendererComponent
method using switch case (as column counted).
After hours, and hours, I think my current solutions is quite daunting tasks. Thus, My question is:
What are the simplest method of creating this Custom JTable to achieve the main goal as mentioned above?
you have two options
1) JPanel nested another JComponents and solve that by using standard LayoutManagers note scrolling isn't natural nor nice
2) JTable with JPanel can solve that, notice about scrolling inner JScrollPane inside another JScrollPane
I've been facing this problem for a while, and I decided to do it myself. Extending the existing implementation of a table, adding some concepts for what I expect from a table, and writting some editors/listeners for that. All the same, but with a treetable.
I'm working on this project called SUMI.
It contains a java package (ar.com.tellapic.sumi.treetable) that is an extension of a JXTreeTable from SwingLabs.
The project is being developed and I didn't provide any documentation yet. You can do what you want by creating a renderer and if needed, an editor, for lastly attaching actions to each object.
If you decide to use it and you need help, email me, I'll help you without any problem.
Or, you could read the source by your own.
Regards,
EDITED (again):
To clear a little bit this answer, I've just created a wiki page in the project wiki and put the relevant code there. If someone feels that the code should be inserted here, please let me know.
Basically, I try to explain how to find a straight solution to the renderer/editor problems you may find using JTable with your specifics needs by using part of my project, in order to get something like this:
Note that the screenshot was taken after clicking on the respective tick-button.
Once you create a nested panel for one row, as suggested by #mKorbel, you can add any number of them to a GridLayout(0, 1) in a JScrollPane. If rendering many rows becomes an issue, you can adopt the same approach used by JTable, illustrated here.
Even though, JTable can be customized to whatever you desire through cell renderer and cell editors, it is never preferred because you have to do a lot of messy codings for that. Instead, for your problem, I suggest to use JScrollPane and add your component (view panel as your sample jTable ) to its viewPort.
For this implementation, represent each rows with your custom class that extends JPanel. And add the required row components (that may be any components like jlabel, jtextfields or even jpanel too) in it. For the simplicity, you can use null layout for the row panel and add the components at any location you want.
I hope this will help you workout with your problem. If you got any problem in this implementation, feel free you ask again.

swing tableChanged() not responding to table changes

I'm coding in Swing in Java. I'm using the Netbeans layout manager. I'm having trouble with a jTable. I've applied a customer model to it which extends AbstractTableModel. I want the third column to contain boolean values in the form of checkboxes (this I have done successfully). The dialog I have the jTable in implements TableModelListener. My tableChanged() method has only the following code: System.out.println("Table changed!");
However, whenever I try to check one of the checkboxes, it does the little "suppression" thing when I click and hold, then when I release, it doesn't change the checkbox's state. It also does not print out "The table has changed!" This has been driving me crazy. I've read all about it, but can't figure out why mine's not working. Please help. Here's the relevant code:
In jDialog constructor:
this.chapterTableModel = new ChapterTableModel(chapterList);
chapterTableModel.addTableModelListener(this);
And then a method which does this: chapterTable.setModel(chapterTableModel);
Then below my constructor, I have this method:
#Override
public void tableChanged(TableModelEvent tme) {
System.out.println("Table Changed!");
}
The entire code can be found here: http://collabedit.com/ttcds
and here: http://collabedit.com/qn3kx
Thanks for your help in advance!
Are you calling one of the fire-methods of the parent AbstractTableModel class in the mutators of ChapterTableModel?
You are not overriding setValueAt anywhere so the value in your table isn't being changed.

Making a cell visible in JTable when AbstractTableModel is extended

I have extended the AbstractTableModel to suit my requirements. Now this table can be altered by other methods of my GUI. I want the table to scroll to the currently edited cell into view. To do this, I think I have to first get the JViewport of the current JComponent, but I see no method by which I can achieve this? How do I achieve this?
I have already done this when I have used the default JTable, but how do I do this when we extend AbstractTableModel?
Models are designed to store data and notify views when the data has changed. It notifies the view of a change in data by firing events. It is the responsibility of the view to listen for these events. Therefore the model never knows directly what view is being updated. This type of functionality should NOT be part of the model.
One approach might be to use a TableModelListener. You can create a TableModelListner with the table as a paramenter. Then when data is changed the listener will be notified. You can then invoke table.scrollRectToVisible(...) on the table. However, with this approach you can't distinquish between edits that have been applied directly through the TableModel versus udates that have been done through the JTable itself.
You may want to have your table fire an event, and have your parent component listen for that event, and scroll accordingly. That way your table doesn't need to know about its parent scroll pane.
You can make use of the EventListenerList in DefaultTableModel to notify any listeners.

In Groovy SwingBuilder, how do I attatch a closure to a JTable that fires when a cell is selected?

I have a JTable being constructed via Groovy's SwingBuilder. I'd like to attach a closure to the table that fires when a cell is selected, but I can't seem to find the right hook.
How do I do that?
I'm not an expert in groovy, but when inside the table element of the swingbuilder, you could use the Groovy way to implement interfaces. This works because ListSelectionListener only has one method.
table(id: 'myTable') {
myTable.selectionModel.addListSelectionListener({evt->
println("selection changed")
} as ListSelectionListener)
}

Categories