Reduce number of getTableCellRendererComponent calls - java

I am using a custom cell renderer that implements TableCellRenderer and displays JTextArea (instead of JLabel) for each row. I am basically overriding getTableCellRendererComponent(...) method with mine which does some additional calculations per row. These calculations have to be done just once per table update. Since getTableCellRendererComponent method is being called with every mouse move, lag occurs. So I thought I should prevent getTableCellRendererComponent from being called to avoid lag.
Considerations:
1) My table has only 1 column and has no header.
2) My data is static and is read from an ArrayList by getValueAt(int row, int column) method in a custom tablemodel class implementing AbstractTableModel.
3) I don't need to watch over mouse motion events.
4) I don't expect much data, so I might want to display whole table at once or cache it completely.
5) Most lag is caused by setting text each time when returning from getTableCellRendererComponent, because some rows are using Right-to-Left chars and RTL text requires extra time to render.

Do not try to limit the number of getTableCellRendererComponent calls. Instead, make your implementation of the renderer better by caching the results of the calculation.
You can easily add a listener to the tablemodel so that your renderer knows when the model is updated. Only then it should mark that the stored calculation results are invalid, and recalculate them on the next getTableCellRendererComponent call.

Related

tableChange() executing when the JTable is refreshed

In my form I have a JTable with a TableModelListener. The tableChanged method updates the logic on my JButtons. This all works correctly. When a user edits a value in a cell in the jtable - the tableChanged method executes and the buttons are refreshed accordingly.
The problem I am having and it is a show stopper. The JTable displays objects and some attributes of the object. The user selects the objects from the application.
If I there is a object selected and being displayed in the jtable. If that user changes a attribute value in the application and not edit it in the JTable. The jtable is still refreshed and the changed value is displayed. But a TableModelEvent is not taking place and my button logic is never refreshed.
I have looked at TableCellListeners - but that is still looking for a edit in the table. So I don't think that will work here.
How can I tell that something has changed and the Table has been updated without a Event taking place in the jtable itself?
EDIT: Placing some of the jtable code
This is in my base dialog class
selectTable = new JTable(SingletonSelectTable.getInstance());
selectTable.getModel().addTableModelListener(this);
selectTable.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component comp = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Font font = comp.getFont();
if (SingletonSelectTable.getInstance().isCellBold(row, column) == true){
comp.setFont(font.deriveFont(Font.BOLD));
}
return comp;
}
});
selectTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
selectTable.setCellSelectionEnabled(false);
selectTable.setRowSelectionAllowed(true);
selectTable.setColumnSelectionAllowed(false);
JScrollPane ScrollPane = new JScrollPane(selectTable);
the tableChanged method
public void tableChanged(TableModelEvent e) {
setRemoveButtonVisibility();
setRemoveAllButtonVisibility();
setCommentButtonVisibility();
setOKButtonEnabledStatus();
}
my singleton class
public class SingletonSelectTable extends AbstractTableModel {
I hope this helps
How can I tell that something has changed and the Table has been updated without a Event taking place in the jtable itself?
There is never an event in the JTable. It is the underlying TableModel that changes, and it is the TableModel that fires an event.
The JTable registers a listener to the TableModel so it knows when it should update the displayed table content.
So if you are saying that the JTable gets updated (automatically, so without you scrolling/selecting/hovering/...) means that the TableModel does fire an event, meaning you can also listen for it.
In the scenario an element of your TableModel is changed in your application, something should fire the TableModelEvent from the TableModel. Typically this is done by either letting the TableModel listen for changes in the objects it contains, or letting the code that changes the object also notify the TableModel that the underlying data has been changed. Either way, the TableModel needs to fire an event, so there is no reason why your listener would not receive it, unless it is simply not fired (meaning an incorrect TableModel).
My best guess, based on your comments, is that you have an incorrect implementation of your TableModel and that your TableModel simply does not fire an event, and that the JTable gets updated 'by accident' (in my experience is a JTable rather robust for missing events, certainly when no rows are added/removed ... definetely compared to a JTree)
The getValueAt() method picks up the change and then updates the jtable
This really sounds incorrect. The getValueAt() method is normally called by the JTable after it receives an event. Thanks to the event, the JTable knows it must be updated so it queries the model for the new data. So the getValueAt method does not pick up the change, but gets called as a result of the change.
I would strongly suggest to take a look at the Swing table tutorial, and certainly the sections about Creating a table model, Listening for data changes and Firing data change events

JTable help (resizing of headers and data source)

I am quite new to JTable and am perplexed over the plethora of information on it. There are several questions that I wish could be answered by you guys.
How can I set each cell value individually? Is it through implementation of a TableModel (I don't quite know how to use this)? I understand that a 2D matrix can be used to set the data beforehand.
How can I make it such that the below table's headers are nicely positioned(the entries are fully displayed)? If you look carefully at the first entry in the table, Job Grade is hidden partially. I have searched high and low for solution to this but to no avail.
http://i45.tinypic.com/24pxvmx.jpg
The concept is that you subclass DefaultTableModel with your own class. If you wanted to show a table of jobs for example, you might create a class called JobsTableModel. The JobsTableModel would typically have a collection of objects of type Job which might be passed in to the constructor. Your JobsTableModel has to be able to provide answers to the following questions:
How many rows are there
How many columns are there
What is the name of column X
What is the data type of column X
What is the value in cell (X,Y)
It does this by overriding the following methods:
public int getRowCount()
public int getColumnCount()
public String getColumnName(int columnIndex)
public Class<?> getColumnClass(int columnIndex)
public Object getValueAt(int rowIndex, int columnIndex)
The JTable implementation can then use this information to populate the table without you having to worry about setting the values in each cell. Furthermore, the JTable implementation will automatically give you sorting and column re-ordering for free. If you had to populate each cell directly, these would be a nightmare to work with.
As regards getting width right, I don't believe there's any automatic way of doing this, you just have to use some trial and error. You can set the width of column in pixels as follows:
TableColumn column = table.getColumnModel().getColumn(i);
column.setPreferredWidth(width);
As I commented above, the best thing you can do is go through http://docs.oracle.com/javase/tutorial/uiswing/components/table.html.
But to answer your two specific questions:
1) Yes you need a TableModel. You can create a new JTable by passing it arrays, but it just creates a DefaultTableModel in the background anyway. Look up Model-View-Controller architecture for more info on why you need the model.
2) You can set a preferred width on the columns with something like this
table.getColumnModel().getColumn(1).setPreferredWidth(100);
Which will give column 1 a preferred width of 100 pixels (I think it's pixels... don't quote me on that though!).
JTable is one of the most sophisticated components of Swing. So takes some time to give it a kick start. But when you get the handle of it, you will see that JTable is very strong and flexible. Having said that, let me try to answer your questions.
1)How can I set each cell value individually? Is it through
implementation of a TableModel(I don't quite know how to use this)? I
understand that a 2D matrix can be used to set the data beforehand
JTable uses a TableModel instance for the data. If you have an available array or Vector with pre-loaded data, you can simply use DefaultTableModel. If DefaultTableModel is not flexible enough for your requirements, then do not hesitate to implement your TableModel by extending AbstractTableModel. AbstractTableModel has only the following three abstract methods, so it is really easy to implement a concrete TableModel by extending AbstractTableModel. :
public int getRowCount();
public int getColumnCount();
public Object getValueAt(int row, int column);
2)How can I make it such that the below table's headers are nicely
positioned(the entries are fully displayed)? If you look carefully at
the first entry in the table, Job Grade is hidden partially. I have
searched high and low for solution to this but to no avail.
http://i45.tinypic.com/24pxvmx.jpg
You can find an example for automatically setting column widths here: http://www.exampledepot.com/egs/javax.swing.table/PackCol.html
The code example in this link uses all column data for calculating preferred column width. I guess it can be adapted for your requirement as well.

JTable erratic behavior

I have some old Java application that uses JTable to show some data and allow input as well.
JTable has 2 columns. Next to JTable, there is button that adds new row into JTable.
My problem is behavior of Tab and Enter keyboard keys and mouse clicks when you navigate JTable.
I will try to explain:
Let say there are 5 rows in JTable:
if I click with mouse on one row, sometime whole row gets highlighted, and sometime cell gets into edit mode (in addition to whole row being higlighted)
Sometime, selected row gets highlighted, but cell above or below highlighted row gets into edit mode
If I use Tab to skip from one field to another, editable field is always above highlighted row.
There are other issues as well.
Any ideas what might be wrong with it?
That sounds a bit odd. Are you fully invalidating your table and notifying listeners etc that the number of rows have changed?
In your table model, loop over all of your TableModelListeners and fire a TableModelEvent 'insert' event:
TableModelEvent event = new TableModelEvent(
modelInstance,
positionOfNewRow,
positionOfNewRow,
TableModelEvent.ALL_COLUMNS,
TableModelEvent.INSERT);
for (TableModelListener l: listeners) {
l.tableChanged(event);
}
If you can't work out the problem, it is possible to write your own behaviour for the table.
These are some useful methods:
editCellAt(int row, int column);
rowAtPoint(Point point);
columnAtPoint(Point point);
You can add your own listeners to the table to intercept events and edit cells in any way you like.
Turn off the Table cell editing. And see how it behaves. Typically this can be handled by changing the TableModel.isCellEditable() method to simply return false.
Turn it back on. Is your instance of JTable subclassed? Is it overriding editCell()? If so that's the method that will trigger an edit based on the event occurring or not. That method is turning on editing inappropriately. The implementation of that method is bad if it's overridden.
If your table isn't subclassed look for calls to editCell(). Those are probably calling it inappropriately so you can look for those calls and start setting breakpoints or log statements.

Java Behavior - JTable and TableCellRenderer

I have a custom JTable (15 rows by 20 cols) that was created to work for all JComponents. I'm currently using it for a mixture of JComboBoxes, JTextFields, and JButtons. Oh, and I'm using Java5 (a requirement).
I have two questions:
1) The first regards the TableCellRenderer class, and its single method:
public Component getTableCellRendererComponent(final JTable table,
final Object value,
final boolean isSelected,
final boolean hasFocus,
final int row,
final int column) {...}
All it does is cast the Object value argument to a JComponent, and potentially change the background color. No big deal. The question I have is why is this method is called sooooo often. When selecting a single cell, it is called 23 times. When Alt-Tabbing between two UNRELATED applications (I use Win7), this method is called over 200 times (and only for JButtons and JTextFields)!
Is this in any way necessary, and if not, how can I put a stop to unnecessary rendering calls?
2) The second question regards the JTable itself. When I'm editing a cell (cursor in a JTextField and blinking) and I click on another cell, that cell is only selected. If I then click on another cell, however, I start editing that cell. All I can think is that from the initial editing component, I'm selecting the JTable, and then selecting the component within. Is there any way to change this? Either going one way (always selects the jtable on first click) or the other (always enters cell on first click). I would prefer the first option, if possible.
Thansk to anyone who can grant some insight/help!
1) Why are you storing Components in the TableModel? That is not efficient. You should be storing data in the TableModel. JTable was designed to render data using a single component. The default renderer is generally a JLabel. Then when you edit a cell a different component is used.
When you click on a cell you may need to re-render the previous row (to remove the row selection) and then render the current row with the selection. So the renderer is called for each visible cell that is affected. Tabbing to the application probably causes all the visible cells to be re-rendererd.
2) Hard to answer since this is not the default behavour. You must also be using custom editors and I don't know what your custom code looks like. The default editor has a setClickCountToStart() method which defaults to 2. Maybe you set this to 1.
Change your solution to use data (not Components) in the TableModel and post your SSCCE if you have further questions.

Working with data and a JTable?

I have a JTable that I want to use to display some data (a String and a Boolean in each row). The data is maintained by my own class. Is there some way to bind the data model to the JTable, so that when I add to the model, the JTable is dynamically updated and when I remove something from the model, the row is removed from the JTable?
I have previously worked with Flex and Actionscript, and this is very easy to do there with data binding, so I'm just wondering how it's done in Java.
Thanks.
You will need to have your dataset implement the TableModel interface. if you do that then you can apply it to the JTable. If you extend AbstractTableModel you will inherit some event firing methods that your table will handle and will update the view. see this tutorial. Note that the default implementation of JTable will renderer your data for you, and if a Boolean is found, it will show up as a check box.
You'll probably find both the Java JTable tutorial and the JTable API documentation helpful in understanding how JTable works, but otherwise here's a quick rundown.
The premise of a JTable is that it is paired with an object that implements the TableModel interface, which by default is an instance of DefaultTableModel. The table model object is made up of a list of columns, each of which has its own data type (String and Boolean in your case), and a list of rows containing the actual data for the table.
Whenever the JTable is drawn by the swing drawing code, it repeatedly calls the method:
public Object getValueAt(int row, int col)
Thus, when you add data to the table model, it is always rendered as you expect in the next screen refresh (dynamically).
The only thing you really need to worry about, then, is getting the data from your object into the table model and back out again. Other than that, JTable takes care off all the heavy lifting.
While implementing TableModel is easy enough for simple cases, you might want to consider a true binding approach (my favorite is Glazed Lists - watch the 30 second video on how easy this is and you'll be won over). Beans Binding (now Better Beans Binding) also has an implementation of observable lists that might be useful (although I much prefer the Glazed Lists approach)

Categories

Resources