I have a JTable with a model created like this :
DefaultTableModel model = new DefaultTableModel(new Object[][] {},new String[] {"Col1", "Col2", "Col3", "Col4", "Col5", "Col6", "Col7", "Col8"}) {
#Override
public Class<?> getColumnClass(int column) {
switch (column) {
case 0: return ImageIcon.class;
case 7: return JButton.class;
default: return String.class;
}
}
};
table = new JTable(model);
But when I use the addRow method, My ImageIcon appears properly but not the JButton. In fact, I get something like javax.Swing.JButt... instead of the JButton!
When you are adding a new row, it is being populated correctly. (You can see the ImageIcon).
So, the JButton object is also being inserted correctly.
The only difference here, JTable does not know how to display it or handle events on it.
That is why JTable calls toString() method on JButton and displays the text (as you mentioned).
javax.swing.JButt......
To overcome this problem, we need to supply two additional objects to the JTable.
TableCellEditor to handle events.
TableCellRenderer to handle painting.
You can find additional information about implementation details here : How to use custom JTable cell editor and cell renderer
Hope this helps.
Good luck.
Edit in response to the comment.
table.getColumnModel().getColumn(7).setCellRenderer(new MyCellRenderer());
// Put painting logic in MyCellRenderer class.
This way you can assign different renderer objects to different columns.
The problem is that JTable doesn’t support a button renderer or editor. You can overcome it using a custom cell editor, ButtonColumn : http://tips4java.wordpress.com/2009/07/12/table-button-column/
Related
I am a software developer apprentice and have to write a graphical project specific configuration editor for my company. I load the data from the configuration excel file of the project with Apache POI and wrap the data into ConfigValue Objects. For different ConfigValue objects there has to be different cell editors and renderers...
The GUI of my programm uses a custom JTable and DefaultTableModel. Every value in the table / model is a ConfigValue which should rendered differently for defined different ConfigTypes. (As far I got it all working - import, wrapping, load into table)
But I have some problems with the TableCellRenderer or TableCellEditor of one of the custom types which should rendered as a ComboBox which contains all possible backend entity values. The ComboBox gets rendered and the correct beginning values are displayed... But when I change one cell to another ConfigValue... The renderer does not display this value... (it always changes to the same value (first of the editor's value) for a cell)
Can anyone help me out what I am doing wrong with my Editor/Renderer?
public class ConfComboBoxCellEditor extends DefaultCellEditor {
public ConfComboBoxCellEditor(List<ConfigValue> possibleValues) {
super(new JComboBox(possibleValues.toArray()));
}
#Override
public Object getCellEditorValue() {
Object cellEditorValue = super.getCellEditorValue();
System.out.println("DEBUG - CELL EDITOR - get editor value --> " + ((ConfigValue) cellEditorValue).toString());
return cellEditorValue;
}
}
public class ConfComboBoxCellRenderer extends JComboBox<ConfigValue> implements TableCellRenderer {
public ConfComboBoxCellRenderer() {
System.out.println("NEW CELL RENDERER");
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
ConfComboBoxCellRenderer renderer = (ConfComboBoxCellRenderer) table.getCellRenderer(row, column);
renderer.removeAllItems();
renderer.addItem((ConfigValue) value);
renderer.setSelectedItem(value);
System.out.println("DEBUG - CELL RENDERER " + row + ", " + column + " - get cell render comp --> " + ((ConfigValue) value));
return this;
}
}
Can anyone help me out what I am doing wrong with my Editor/Renderer?
JTable support JComboBox as TableCellEditor, there isn't any issue to set different dataset for each of JComboBoxes used as TableCellEditor
TableCellRenderer only shows, painting the value stored in DefaultTableModel, then every code lines inside renderer.xxxXxx are missinterpreting of Renderers Concept in Swing, contraproductive and could be heavy tasks, Renderer isn't place to set/getValue, a new event is fired from all mouse/key events from all cell are visible in JViewport, plus internal events from JTable/TableModel APIs,
your Renderer isn't about how to painting JComboBox as rendering component
nothing cleaver, nor specifics without an SSCCE/MCVE, short runnable, compilable with hardcoded value for JTable/DefaultTableModel in local variable
i am making a twitter client (desktop application) in Java, i am using twitter4j API also. i have managed to do the search for tweets and i get back results and i show them in a Jlist.
what i want is that i want to show tweets nicely in the list, not only as a text .. show the image of the user, the tweet, tweeted by ... etc all this information .. in addition attach additional data like star rating .. how can i add that to a JList ? can the Jlist hold different objects .. Jpanels for example ..
Instead I suggest you put a set of JPanels inside a JScrollPane.
A JList's renderer must be a JComponent, so you can use any Swing object, including JPanels.
You can also use HTML in a JLabel if it is easier to do so than using a JPanel.
To use a custom renderer, you do something like this..
myList.setCellRenderer(new CustomRenderer());
and then create a renderer like this
public class CustomRenderer extends DefaultListCellRenderer {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean hasFocus) {
JPanel panel = new JPanel();
// set up the panel for your exact display requirements.
return(panel);
}
}
Suggest using a JTable, which has several columns, instead of a JList.
Also suggest using GlazedLists, which makes it really easy to display lists with fields in a JTable, so that they update automatically when the underlying list changes.
Here's an example of some code I wrote recently which displays something similar:
private void bindEmailTargetTable(NotificationModel newModel) {
JTable table = getUI(UIKey.EMAIL_TARGET_TABLE);
EventList<EmailTarget> displayList = newModel.getEmailTargets();
TableFormat<EmailTarget> tf = new TableFormat<EmailTarget>()
{
#Override public int getColumnCount() {
return 4;
}
private final String[] columns = { "address", "description", "msg left", "msg limit" };
#Override public String getColumnName(int col) {
return this.columns[col];
}
#Override public Object getColumnValue(EmailTarget item, int col) {
switch (col)
{
case 0:
return item.getAddress();
case 1:
return item.getDescription();
case 2:
return item.getRemainingMessages();
case 3:
return item.getMessageLimit();
default:
return "";
}
}
};
EventTableModel<EmailTarget> etm = new EventTableModel<EmailTarget>(displayList, tf);
table.setModel(etm);
}
That's 33 lines of code to take a JTable and make it automatically update itself to display 4 fields of each EmailTarget in an EventList<EmailTarget>.
For non-text field contents, you just need a custom TableCellRenderer.
AS Jason suggested its better to go for jtable instead of JLIst. In fact you can use any free Java based table classes that have extended functionality over JTables. JIDE is one such library but its commercial. you can search and find a lot..
I have a DefaultTableModel which is populated with an Object[][] array.
Now I want to add a column with checkBoxes and perform operations accordingly.
When I add the checkbox into the Object[][] array and view it, I get text displayed
'javax.swing.JCheckBox[,0,0,0x0....', how do I get it to show a checkbox and add actions to it?
JTable have default checkbox renderer/editor for boolean values. Just make your TableModel#getColumnClass return Boolean.class for given column.
how do I get it to show a checkbox
See Uhlen's answer
and add actions to it?
Use a TableModelListener. Something like:
public void tableChanged(TableModelEvent e)
{
if (e.getType() == TableModelEvent.UPDATE)
{
int row = e.getFirstRow();
int column = e.getColumn();
if (column == ?)
{
TableModel model = (TableModel)e.getSource();
Boolean value = (Boolean)model.getValueAt(row, column));
if (value.booleanValue())
// add your code here
}
}
}
You could also just get the class, instead of hard coding each return type. Here is an example for the override method :
//create the table
DefaultTableModel tableModel = new DefaultTableModel(data, columnNames)
//override the method
{
public Class<?> getColumnClass(int colIndex) {
return getValueAt(0, colIndex).getClass();
}
Then, when you create the table you initialize it this way:
data[i][12] = new Boolean(false);
which makes the box appear unticked :)
You could use a custom table cell renderer.
See here
http://www.exampledepot.com/egs/javax.swing.table/CustRend.html
No you cannot provide swing component as model object[] array. That should be registered as cell editor on column.
Anyway by default DefaultTableModel supports checkbox as editor for columns under which Boolean class type values are stored.
So, in the array pass Boolean.TRUE/Boolean.FALSE object and set table as editable. Then table automatically renders checkbox for you.
Are you need to register editor for each class type
I am using Netbeans and am trying to find a way for the IDE to auto-generate the code for me. I remember binding a JLabel's text to a column in the selected row of the JTable before, but in that case, the JTable's values were from an entity manager, and it was very easy. I was wondering if there is a way to do it even if the JTable is not tied to a database.
Also, how else could one do it? I was thinking of implementing a ListSelectionListener, and whenever an event got generated, just update the text of the label.
I think your second solution is best way to do it, something like this:
public class LabelSyncer implements ListSelectionListener {
private JLabel toSync;
private int columnIndex;
public LabelSyncer(JLabel toSync, int columnIndex) {
}
public void valueChanged(ListSelectionEvent e) {
JTable table = (JTable) e.getSource();
int row = table.getSelectedRow();
toSync.setText(table.getModel().getValueAt(row, columnIndex).toString());
}
}
and then
table.getSelectionModel().addListSelectionListener(new LabelSyncer(label, columnIndex));
Something like this. Probably a more generic solution, but this should work.
The following ListCellRenderer does not receive click events on the nested ComboBoxes. Do I need to enable something?
class FilterCellRenderer implements ListCellRenderer {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Filter filter = (Filter)value;
JPanel filterPanel = new JPanel();
FlowLayout layout = new FlowLayout();
layout.setAlignment(FlowLayout.LEFT);
filterPanel.setLayout(layout);
filterPanel.add(new JLabel(filter.getLabel()));
final List<Object> options = filter.getOptions();
if (options.size() > 1) {
JComboBox optionCombo = new JComboBox(new AbstractComboBoxModel() {
public int getSize() {
return options.size();
}
public Object getElementAt(int index) {
return options.get(index);
}
});
optionCombo.setSelectedItem(filter.getValue());
filterPanel.add(optionCombo);
}
if (isSelected) {
filterPanel.setBackground(list.getSelectionBackground());
filterPanel.setForeground(list.getSelectionForeground());
}
return filterPanel;
}
}
Renderer components in swing work like "rubber stamps" -they are just used to render/paint a value and are not added to the parent container in the usual way (just think how a single component could be added in multiple places!).
It sounds like you may want an editor rather than a renderer (an editor is a fully-fledged component, added in one place at any given time). Failing that you will have to install the MouseListener on the JList instead.
Since I didn't need to select rows, I ended up just dynamically adding and elements to a JPanel with a custom layout. Allowed for full component behaviour without having to hack a table.
It's a little bit tricky this. I believe you need to replace the JList with a single column JTable. Then set a table cell editor as well as renderer. IIRC, there might be a problem losing the first click (which gets used to select that cell edited).
Also it's a very good idea to reuse the components between each call to getCellRendererComponent. The components are used as a stamp and then discarded. Performance will suck massively if they are recreated each time.