I want to make a JTable cell renderer which will be able to recognize a type of data and use a appropriate representation.
More specific, i want my table to be able to recognize if type of data is Collection and in that case to use JComboBox for representation (in other cases a standard representation: JCheckBox for boolean type and JLabel for other types and for that I'm using custom model's method getColumnClass())
I don't know the type of data in advance, in which column will be Collection or even is there will be Collection at all so I can't use a standard way of
JComboBox cell renderer TableColumn column = table.getColumnModel().getColumn(1);
or similar which I'm familiar with.
Please help :)
You can use the DefaultRendering mechanism inside JTable, reusing this mechanism to apply for each cell:
tableView.setDefaultRenderer(TitleViewCell.class, new TitleTableCellRenderer.class());
tableView.setDefaultRenderer(DataViewCell.class, new DataTableCellRenderer.class());
Here you can subclass a JTable into a JTableView for example:
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
public class JTableView extends JTable
{
private static final long serialVersionUID = -1350656893711208356L;
public JTableView(TableModel tableModel)
{
super(tableModel);
}
public TableCellRenderer getCellRenderer(int row, int column)
{
Object value = getValueAt(row, column);
if (value != null)
{
return getDefaultRenderer(value.getClass());
}
return super.getCellRenderer(row, column);
}
};
Related
I'm using TableCellRenderer to render a button in a cell for a JTable created with Matisse in netbeans.
My problem is ... When a double click on the button, I can reach the text field behind. So I want to set the textfield not editable.
For now, my setEnabled are on true: table_watchlistMain.setEnabled(true); I need that because I want to user to be eable to select a row ...
I'm using a DefaultTableModel... do I need to make my own model?
I'm just searching a solution to put the jtable enabled, but not editable. this is possible??
The DefaultTableModel.isCellEditable() method always returns true:
Returns true regardless of parameter values.
So, yes, you should create your own model, for example:
public class MyTableModel extends DefaultTableModel
{
#Override
public boolean isCellEditable(int row, int column)
{
return false;
}
}
SCCEE here:
import java.awt.EventQueue;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
public class TC extends JFrame{
public TC(){
begin();
}
private void begin(){
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setTitle("nothing.");
String[] options = {"One", "Two", "Three"};
JComboBox<String> combo = new JComboBox<>(options);
JTable table = new JTable(new Object[2][2], new String[]{"One", "Two"});
TableColumn col0 = table.getColumnModel().getColumn(0);
col0.setCellEditor(new DefaultCellEditor(combo));
class MyRender extends DefaultTableCellRenderer {
public MyRender() {
}
#Override
public void setValue(Object value) {
if (value instanceof JComboBox) {
setText(((JComboBox) value).getSelectedItem().toString());
}
}
}
MyRender renderer = new MyRender();
col0.setCellRenderer(renderer);
JScrollPane sp = new JScrollPane(table);
getContentPane().add(sp);
pack();
setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable(){
#Override
public void run() {
TC tc = new TC();
}
});
}
}
My problem is: setting the TableCellRenderer makes the combo choose an empty option atop of all other values, without anyone telling it to do so. The empty entry comes from nowhere.
How can I make the combo select the "One" entry at first moment, instead of " "? Something I missed when implementing the custom renderer?? I followed here:
Oracle tutorial of How to Use Tables - Swing - Java SE
Also, the combo is not shown until I click it. I don't think it's the proper way to show it. I tried to follow another example here:
Show a JComboBox with custom editor and renderer, example from java2s.com
but I remain confused.
How can I make the combo select the "One" entry at first moment,
This is the default behaviour. The data from the TableModel is used to select the item in the combo box when the editor is invoked.
The section from the Swing tutorial on Using a ComboBox as an Editor contains a working example showing how to do this.
the combo is not shown until I click it
This is the way it is designed. The renderer displays the data normally. The editor is not displayed until the user starts editing the cell.
If you want to give the user an indication that a combo box will be used as the editor then you need to use a custom renderer. Your attempted implementation is incorrect because you would never have a JCombobox as data in the table model.
Check out: How to make a JComboBox table editor have the design of an ordinary JComboBox? for a couple of different implementations of a possible renderer:
my example shows what using a real combo box would be like (I don't like it) and
the accepted answer shows a better renderer. You might want to change the example to have a panel as the renderer with a BorderLayout. Then you add a label to the LINE_START and the icon to the LINE_END so the icon so it more resembles a combo box with the down arrow on the right.
Edit:
but in your link, the combobox is showing the first option
No it isn't. Test the code again. Click on the 2nd or 3rd row to invoke the editor to see which item is selected.
To clarify what I said earlier. The item in the combo box is selected based on the data in the TableModel. Since your TableModel is empty there is no item to select.
Put some data in the TableModel. The example from the Swing tutorial and the two examples in the link I provided you all have data in the TableModel which is why they work.
Thank you Rob Carmick. I won't cross-posting again.
I have found the problem. In the default renderer I put one line of code like this:
class MyComboBoxRenderer extends JComboBox implements TableCellRenderer {
public MyComboBoxRenderer(JComboBox<String> combo) {
super(combo.getModel());
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
// setSelectedItem(value); // where lies the problem, will always be null.
return this;
}
}
When the combo is loaded, the value is always null, and I let the combo select it, so the empty line appears. Comment it and all's fine.
In CodeRanch I post this and system takes it as spam, I don't know why. Although I found my solution myself, I accept you answer for helping.
Thanks again.
Is there a way to update the current DefaultTableModel on a JTable? To clarify what I mean I will use some code examples below.
You might declare a JTable like this
DefaultTableModel model = new DefaultTableModel(tableData, tableHeaders)
{
private static final long serialVersionUID = 8785594035471551113L;
#Override
public boolean isCellEditable(int row, int column)
{
return false;
}
};
JTable table = new JTable(model);
panel.add(table);
I am asking if there is a way to do something similar to this
myTable.java
public class myTable extends JTable {
private static final long serialVersionUID = -5819940358496590055L;
public myTable(TableModel dM) {
setModel(dM);
}
public myTable(Object[][] tableData, Object[] columnHeaders) {
this(new DefaultTableModel(tableData, columnHeaders));
}
public void setEditable(boolean b) {
DefaultTableModel model = (DefaultTableModel) this.getModel();
//Do some code here to make the editable or uneditable
setModel(model);
}
}
someJavaFile.java
JTable table = new myTable(tableData, tableHeaders);
table.setEditable(false);
panel.add(table);
I am not asking you to do this for me but I am asking you to aid me in finding a way to update the current DefaultTableModel.
I appreciate any help
Edit for clarification
I am trying to find a way to manipulate the current DefaultTableModel on a JTable for things such as whether the table is editable or not as there are no methods which support toggling whether the table is editable after the model has been made. There is only isCellEditable().
Why would this be useful?
This would be useful if you were wanting to manipulate the way the table worked with an ActionListener on a JButton or something similar
things such as whether the table is editable or not as there are no methods which support toggling whether the table is editable after the model has been made
You will need to provide a custom TableModel. You can build this functionality into the DefaultTableModel by extending the model and adding a couple of methods.
Or you can check out the Row Table Model.
Among other features it allows you to make the entire model editable or not. You can also control whether individual columns are editable or not.
So i am working with a JTable, It has Columns A-K. with A and B being the only editable ones. If someone edits an empty row in A, I make an API call to get B then i make a DB call to get all rows where B exists.If someone edits an empty row in B, i make the same call as the will be retrieved from the DB for that row as well. The call returns 0-N rows. If 0 rows were returned, I change the values of all row except B to N/A otherwise i populate the rows using the data.Once populated, i make all columns non-editable. The DB call occurs in its own thread as once the call is return i create my own record object which I add to the tablemodel.
I have my own TableModel and a TableModelListener to keep the data and handle changes in values.
Here is my issue. I am using TableCellRenderer and using the cellrenderer to see if the value was changed, if so then i make the calls and populate as needed. When a large number of rows is being pulled from DB, it takes a while to load and making all that records so I tried to use a ProgressBar to show the user that the screen isn't just frozen, it is progressing and by how much. However the frame that comes up is blank and nothing gets displayed. I get the feeling i am doing something either improperly or missing something.Any help much appreciated.
some code to understand what i am talking about
public class MyPanel extends JPanel {
private JTable myTable;
private MyTableModel tm;
//OTHER FIELDS
public static void createPanel() {
tm = new MyTableModel(columnnames);
myTable = new JTable(tm);
TableColumn account = myTable.getColumnModel().getColumn(
MyTableModel.ACCOUNT_INDEX);
account.setCellRenderer(new MyTableRenderer(
MyTableModel.ACCOUNT_INDEX));
}
}
public class MyTableRenderer extends DefaultTableCellRenderer{
protected int interactiveColumn;
public MyTableRenderer(int interactiveColumn) {
this.interactiveColumn = interactiveColumn;
}
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
Component c = super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
if (column == interactiveColumn && hasFocus) {
//DO DB and API CALLS HERE
//IF DB CALL DISPLAY A NEW FRAME WITH PROGRESSBAR
}
return c;
}
}
Sorry for formatting issues
Use SwingWorker, which allows you to update your TableModel as you examine your result set.
Addendum: Don't try to update the TableModel from the renderer. You can update the model when your implementation of CellEditor has concluded, by starting a suitable worker in getCellEditorValue(). In that way, the revised data will be available when the renderer is next invoked for any modified cell(s). This related example outlines the approach.
Addendum: getCellEditorValue() is invoked after editing has concluded, but starting the worker in setValueAt() offers more reliable access to the target row and column.
It is weird that I set my JTable cell renderer like this:
setDefaultRenderer(Object.class, new MyTableRenderer());
My table renderer works like this:
class MyTableRenderer extends 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 (table.getModel().getValueAt(row, 0).equals(BUY)) {
comp.setFont(font.deriveFont(Font.BOLD));
comp.setForeground(BUY_COLOR);
}
else {
comp.setFont(font.deriveFont(Font.BOLD));
comp.setForeground(SELL_COLOR);
}
return comp;
}
}
But it turns out, it didin't apply those columns that have type "BigDecimal", Other String fields are all working fine.
And then, I add one more line:
setDefaultRenderer(BigDecimal.class, new MyTableRenderer());
Then everything just work fine.
Why it is like this?
JTable by default installs a renderer for type Number. BigDecimal is-a Number so the default renderer is used instead of your custom renderer.
BTW: your custom renderer is buggy in that it doesn't take potentially sorted/filtered rows into account (the row/column index params of the method are view coordinates).
#eugener: your answer is wrong - it's not the storage that's important but the lookup ;-)
Cheers
Jeanette
Take look at the source code of the JTable:
public void setDefaultRenderer(Class<?> columnClass, TableCellRenderer renderer) {
if (renderer != null) {
defaultRenderersByColumnClass.put(columnClass, renderer);
} else {
defaultRenderersByColumnClass.remove(columnClass);
}
}
It uses a map where column class is a key and means that class comparison is literal. This should explain the reason for behavior you're experiencing. This is by design.