I have a JTable which contains a Name and Choice column. I would like for the Choice column to contain a JComboBox Component that has the same selection for every row, although allow for independent selection for every unique row.
Currently, interacting with the JComboBox within the column allows for the drop-down to appear for a selection to be made; although, no selection is saved.
Instead, the selection made migrates to any JComboBox that I click on. E.g., I click on the first row's JComboBox and select "Choice B", but all choices still appear as "Choice A". It isn't until I click on another row's JComboBox that the drop-down appears with the "Choice B" selection highlighted.
The code used for this table is as follows:
final String[] choices = new String[]{"Choice A", "Choice B", "Choice C"};
final Collection<String> mockData = Arrays.asList("First", "Second", "Third", "Fourth", "Fifth", "Sixth");
table.setModel(new MasterTableModel(mockData.stream().map(s -> {
return new Object[]{s, new JComboBox<>(choices)};
}).collect(Collectors.toSet())));
table.setDefaultEditor(JComboBox.class, new DefaultCellEditor(new JComboBox<>(choices)));
table.setDefaultRenderer(JComboBox.class, new MasterTableComboRenderer(table.getDefaultRenderer(JComboBox.class)));
I have three choices, which are used to initalize JComboBoxes that populate the Choice column.
I set the DefaultRenderer to my custom MasterTableComboRenderer object which implements the TableCellRenderer interface so as to have the JComboBox show up as a Component rather than to print the address of the object. It only requires the overriding of a single method:
class MasterTableComboRenderer ...
#Override
public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) {
return value instanceof Component ? (Component) value : renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
Finally, MasterTableModel is a simple extension of AbstractTableModel, which defines the column headers [Name, Choice] and holds an Object[][] reperesenting the JTable's data.
I've overriden isCellEditable and getColumnClass as follows:
class MasterTableModel...
#Override
public boolean isCellEditable(final int rowIndex, final int columnIndex) {
return getColumnName(columnIndex).equals("Choice");
}
#Override
public Class<?> getColumnClass(final int columnIndex) {
return getValueAt(0, columnIndex).getClass();
}
Is there something I've been missing to achieve the functionality of the JComboBox saving its choices and not having the selection highlight migrate to other boxes?
A JComboBox should NOT be stored in the TableModel. The String value is stored in the model.
Read the section from the Swing tutorial on Using a Combo Box as a Renderer for a working example.
If you want the renderer to look like a combo box, then you need to use a combo box as a renderer. Something like:
class ComboBoxRenderer extends JComboBox implements TableCellRenderer
{
public ComboBoxRenderer()
{
setBorder(null);
}
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
removeAllItems();
addItem( value );
return this;
}
}
Related
I have a JTable and I added a DefaultTabelModel to it. I created a popup menu that appears when users right click on a cell in the table. One of the items in the menu is "Rename." How can I make the selected cell editable when the Rename item is clicked? I have set up all the elements and the only missing piece here is making THE selected cell editable.
The isCellEditable(row, col) method in the DefaultTableModel is not helpful here because it sets a cell's editability based on its position (i.e. row and column) in the table, not the selection status of a cell.
I suspect that I will need TableCellEditor, but I am not sure how to use it. I would really appreciate a sample code on how to make this happen and/or explanations of how to use TableCellEditor for this purpose. Thank you in advance!!
Relevant pieces of my code:
class DataListTable extends JTable
mouseReleased():
int row = this.getSelectedRow();
popupmenu.show(this, event.getX(), event.getY());
class RenameDataMenuItem
actionPerformed():
//want to get the (row, col) of the selected cell here and make it editable
How can I make the selected cell editable when the Rename item is clicked?
The isCellEditable(...) method will determine if the cell can be edited via the table editor.
You can still change the data in the model by using the setValueAt(..) method of the JTable or the TableModel.
So what you can do is in your rename menu item you can display a JOptionPane to prompt for the new value. Then you just manually save the value using either of the above methods.
Try something along the lines of this:
public class MyTableCellEditor extends AbstractCellEditor implements TableCellEditor {
JComponent component = new JTextField();
#Override
public Object getCellEditorValue() {
return ((JTextField)component).getText();
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
if(isSelected) {
((JTextField)component).setText((String)value);
}
return component;
}
}
I want to make a JTable , where one single cell (not all rows of a column) will contain a JComponent like JdatePicker or JComboBox.
I have written this code
DefaultTableModel dm;
dm = new DefaultTableModel() {
public boolean isCellEditable(int rowIndex, int columnIndex) {
return !(columnIndex == 0); //make 0th column non-editable
}
};
Object [] columnHeaders=new Object[]{"Field", "Value"};
Object [][] data=new Object[][]{{"ID",""},{"Reg Year",""},{"Reg Date", ""}} ;
regFormTable.setModel(dm); //regFormTable is a Jtable object
I also have a class that implements TableCellrenderer:
public class datePickerCellRenderer extends JFrame implements TableCellRenderer{
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
this.add(new JDateChooser());
return this;
}
}
How can I use datePickerCellRenderer class to implement as I described. I tried a few ways. None of those worked.
[Using Netbeans GUI builder is there a way to do that?]
Plaese ask if any more code I need to add
I would like to make an editable table and then check the data to make sure its valid. Im not sure how to change the color of just one cell. I would like to get a cell, for example (0,0) and color the foreground to red. I have read the other posts on SO as well as Oracle about the custom ColorRenderer, but i just don't get how i would use this.
Thanks.
Say that the cell you would like to render with a different color represents a status (I'll take Rejected and Approved as examples). I'd then implement a method in my table model called getStatus(int row) which returns the status for any given row.
Then, when that is in place, I'd go about creating a cell renderer responsible for rendering the column which the cell belongs to. The cell renderer would be something in the lines of the below code.
public class StatusColumnCellRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
//Cells are by default rendered as a JLabel.
JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
//Get the status for the current row.
CustomTableModel tableModel = (CustomTableModel) table.getModel();
if (tableModel.getStatus(row) == CustomTableModel.APPROVED) {
l.setBackground(Color.GREEN);
} else {
l.setBackground(Color.RED);
}
//Return the JLabel which renders the cell.
return l;
}
Then, when the renderer is in place, simply "apply" the renderer to the table with the following piece of code:
Table.getColumnModel().getColumn(columnIndex).setCellRenderer(new StatusColumnCellRenderer());
With regard to making a cell editable, simply implement the isCellEditable(int rowIndex, int columnIndex) method in your table model. You also need to implement the method
setValueAt(Object value, int rowIndex, int columnIndex) if you would like to keep the value which the user provides (which i assume you do!).
I would like to make an editable table and then check the data to make sure its valid.
Another approach would be to edit the data before it is saved to the table model to prevent invalid data from being entered.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.table.*;
public class TableEdit extends JFrame
{
TableEdit()
{
JTable table = new JTable(5,5);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollpane = new JScrollPane(table);
getContentPane().add(scrollpane);
// Use a custom editor
TableCellEditor fce = new FiveCharacterEditor();
table.setDefaultEditor(Object.class, fce);
}
class FiveCharacterEditor extends DefaultCellEditor
{
FiveCharacterEditor()
{
super( new JTextField() );
}
public boolean stopCellEditing()
{
try
{
String editingValue = (String)getCellEditorValue();
if(editingValue.length() != 5)
{
JTextField textField = (JTextField)getComponent();
textField.setBorder(new LineBorder(Color.red));
textField.selectAll();
textField.requestFocusInWindow();
JOptionPane.showMessageDialog(
null,
"Please enter string with 5 letters.",
"Alert!",JOptionPane.ERROR_MESSAGE);
return false;
}
}
catch(ClassCastException exception)
{
return false;
}
return super.stopCellEditing();
}
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column)
{
Component c = super.getTableCellEditorComponent(
table, value, isSelected, row, column);
((JComponent)c).setBorder(new LineBorder(Color.black));
return c;
}
}
public static void main(String [] args)
{
JFrame frame = new TableEdit();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}
I believe the correct way to do colouring in a table is via a ColorHighlighter. The table renderers have problems to render different colours in the same column.
Here is an example of how to use highlighters. In this case it is for highlighting a cell that is not editable.
public class IsCellEditablePredicate implements HighlightPredicate {
private JXTable table;
public IsCellEditablePredicate (final JXTable table) {
this.table = table;
}
#Override
public boolean isHighlighted(Component component, ComponentAdapter componentAdapter) {
return !table.isCellEditable(componentAdapter.row,
componentAdapter.column);
}
}
and then in your code for setuping the table you add the highlighter and its colour parameters:
ColorHighlighter grayHighlighter = new ColorHighlighter(new IsCellEditablePredicate(table));
grayHighlighter.setBackground(Color.LIGHT_GRAY);
grayHighlighter.setForeground(table.getForeground());
grayHighlighter.setSelectedBackground(table.getSelectionBackground().darker());
grayHighlighter.setSelectedForeground(table.getSelectionForeground().darker());
table.setHighlighters(grayHighlighter);
This is the simplest way to color a particular Column or cell in a jTable.
First just create a simple class of CustomRenderer
class CustomRenderer extends DefaultTableCellRenderer <br />
{
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);
setForeground(Color.blue); >
return c;
}
}
This code gets the column of cell to render
TableColumn col = tblExamHistoryAll.getColumnModel().getColumn(5);
DefaultTableModel model3 = (DefaultTableModel)tblExamHistoryAll.getModel();
col.setCellRenderer(new CustomRenderer());
This is to clear all previous rows from your table. If you do not want them just remove these lines
model3.getDataVector().removeAllElements();
model3.fireTableDataChanged();
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int col) {
Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
int control = row;
control = control % 2;
control = (control == 0) ? 1 : 0;
if (control == 1) {
c.setBackground(Color.green);
} else {
c.setBackground(Color.cyan);
}
return c;
}
The most straightforward way is to write a simple TableCellRenderer by extending the DefaultTableCellRenderer and overwriting the getTableCellRendererComponent method to setBackground( Color.RED ). For example:
final JTable table = new JTable(...);
table.setCellRenderer( new DefaultTableCellRenderer() {
public Component getTableCellRenderer(JTable table, Object value, ...) {
super.getTableCellRenderer(...);
if ( value should be highlighted ) {
setBackground( Color.RED );
}
return this;
}
});
You can extend DefaultTableCellRenderer, override getTableCellRendererComponent and call something like
if (myConditions) setBackground(myColor);
before returning "this" when conditions apply but it has a very annoying side-effect of changing the default back-color due to the way DefaultTableCellRenderer.setBackGround is coded.
The trick I found was to entirely duplicate the code of DefaultTableCellRenderer in a class named HackedDefaultTableCellRenderer, add a method that calls directly the Component's setBackground implementation:
public void setComponentBackground(Color c) {
super.setBackground(c);
}
then derive my customized rendered from this hacked class instead of from DefaultTableCellRenderer, and finally call setComponentBackground instead of setBackground in my customized getTableCellRendererComponent.
The drawback is that this HackedDefaultTableCellRenderer relies on a snapshot of DefaultTableCellRenderer.
Hi I have a class called ColorChooser (in the net.java.dev.colorchooser.ColorChooser package)
This is a custom component used to select colors. What I want is to display a JTable with ColorChoosers in the second column. So I created my own TableCellRenderer and it works:
#SuppressWarnings("serial")
class ColorChooserTableRenderer extends DefaultTableCellRenderer {
public static List<ColorChooser> colors;
public ColorChooserTableRenderer(int rows) {
colors = new ArrayList<ColorChooser>(rows);
for (int i = 0; i<rows ; i ++) {
colors.add(new ColorChooser());
}
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
return colors.get(row);
}
}
I register this in my table :
JTable t = new JTable(5,3);
t.getColumn(t.getColumnName(1)).setCellRenderer(new ColorChooserTableRenderer(5));
The display is good. It even displays the tool tip of the ColorChoosers when i hover my mouse over one of them. The problem is that the ColorChoosers do not receive MouseEvents.
Normally when you press and hold the mouse on a ColorChooser, you get a pop up window that you can use to select a color. When in the JTable the ColorChooser component does not receive the mouse event.
Any solutions?
Edit: The question can be easily modified to this:
Can you please give me a small example of a table containing JButtons in the second column that actually work? You know, buttons that can be pressed?
This sounds vaguely familiar as I have been using table cell renderers for other purposes.
My understanding is that TableCellRenderer is only used to render the component; a component does not actually exist at each of the cells.
So you'd probably have to somehow forward mouse events from the JTable itself to the ColorChooser.
edit: p.s., see my question -- also for custom table cell rendering, you only need 1 instance of the component itself for the entire column, if the column is rendered with the same logic. Don't store persistent state in the TableCellRenderer, store it in the TableModel instead, and use that state immediately prior to rendering when you handle the call to getTableCellRendererComponent().
A renderer only paints the component on the screen and does not allow for interaction. What you need is to also implement a TableCellEditor. It is recommend that you inherit the AbstractCellEditor and you'll save some work. Check out the java tutorial for tables.
Example:
public class MyTableCellRenderer implements TableCellRenderer
{
private JButton button = new JButton("Press Me");
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
return button;
}
}
public class MyTableCellEditor extends AbstractCellEditor implements TableCellEditor
{
private JButton button;
public MyTableCellEditor()
{
button = new JButton("Press Me");
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
System.out.println("buttonPressed");
}
});
}
public Object getCellEditorValue() {
return null;
}
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
return button;
}
}
I want to put individual JComboBoxes into each cells of a JTable. ie. The JComboBox content is not identical for each cell.
I basically would like to be able to just call the following code to add a row of JComboBox into the JTable. Anyone has any idea? Thanks
JComboBox cb1 = new JComboBox(...);
JComboBox cb2 = new JComboBox(...);
model.addRow(new Object[] {"Row name", cb1, cb2} );
JComboBox cb3 = new JComboBox(...);
JComboBox cb4 = new JComboBox(...);
model.addRow(new Object[] {"Row name 2", cb3, cb4} );
The closest example code I can find is as follows. But it is for where JComboBox content is identical for the individual column. Not the solution I need.
TableColumn col = table.getColumnModel().getColumn(vColIndex);
col.setCellEditor(new MyComboBoxEditor(values));
where
public class MyComboBoxEditor extends DefaultCellEditor {
public MyComboBoxEditor(String[] items) {
super(new JComboBox(items));
}
}
Extend JTable with this code:
#Override
public TableCellEditor getCellEditor(int row, int column) {
Object value = super.getValueAt(row, column);
if(value != null) {
if(value instanceof JComboBox) {
return new DefaultCellEditor((JComboBox)value);
}
return getDefaultEditor(value.getClass());
}
return super.getCellEditor(row, column);
}
This will create a unique JComboBox cell editor for each combo box you get the a value for.
I am sure this will solve your problem. Mention in which column you need to set the combo box in .getColumn(int column)
private void addComboToTable(JComboBox combo) {
TableColumn gradeColumn = YourTable.getColumnModel().getColumn(0);
JComboBox comboBox = combo;
comboBox.removeAllItems();
try {
comboBox.addItem("Item 1");
comboBox.addItem("Item 2");
comboBox.addItem("Item 3");
} catch (NullPointerException e) {
} catch (Exception e) {
e.printStackTrace();
}
gradeColumn.setCellEditor(new DefaultCellEditor(comboBox));
}
You need to override:
Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
...in TableCellEditor. The value passed in to this method is what you can put in your JComboBox. That means that the 'value' for that particular cell needs to be something that can be translated into a collection. It could potentially just be a List of objects or it could be a POJO with fields that could be made into a JComboBox.
So just edit MyComboBoxEditor to override that method and change your model to allow for an Object that actually represents several other objects.
The JComboBox content is render identical for each row selection because
the JTable does not offer the capability to have more than one editor per column.
You have to extend the JTable class to support an additional selection for rows.
This article explains it very well:
http://www.javaworld.com/javaworld/javatips/jw-javatip102.html
In addition to cellEditor it is necessary to do the cellRenderer to paint the combobox in the cell, look at this:
public void example(){
TableColumn tmpColum =table.getColumnModel().getColumn(1);
String[] DATA = { "Data 1", "Data 2", "Data 3", "Data 4" };
JComboBox comboBox = new JComboBox(DATA);
DefaultCellEditor defaultCellEditor=new DefaultCellEditor(comboBox);
tmpColum.setCellEditor(defaultCellEditor);
tmpColum.setCellRenderer(new CheckBoxCellRenderer(comboBox));
table.repaint();
}
/**
Custom class for adding elements in the JComboBox.
*/
class CheckBoxCellRenderer implements TableCellRenderer {
JComboBox combo;
public CheckBoxCellRenderer(JComboBox comboBox) {
this.combo = new JComboBox();
for (int i=0; i<comboBox.getItemCount(); i++){
combo.addItem(comboBox.getItemAt(i));
}
}
public Component getTableCellRendererComponent(JTable jtable, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
combo.setSelectedItem(value);
return combo;
}
}
#Override
public TableCellEditor getCellEditor(int row, int column) {
Object value = super.getValueAt(row, column);
if(value != null) {
if(value instanceof JComboBox) {
return new DefaultCellEditor((JComboBox)value);
}
return getDefaultEditor(value.getClass());
}
return super.getCellEditor(row, column);
}
And then, override the toString method from JComboBox.
This page might help you, although it seems you are restricted to having the same combobox in all the cells in a column.
You need to create a subclass of JTable to override the method TableCellEditor getCellEditor(int row, int column).
This enables you to set arbitrary cell editors for any row and column combination. The default way is to set the cell editor for an entire column.
(You can also set individual cell renderers by overriding getCellRenderer.)
The easiest way is to implement your own TableModel
public class MyModel extends AbstractTableModel {
List rows;
public int getRowCount() {
return rows.size();
}
public int getColumnCount() {
return 4;
}
public Object getValueAt(int row, int column) {
return rows.get(row).getCol(col); //assuming your row "Object" has a getCol()
}
public Class<?> getColumnClass(int col) {
return Boolean.class;
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
rows.get(rowIndex).getCol(columnIndex).setValue(aValue);
}
}
Load this into you JTable. If you haven't replaced the default cell renderer for Boolean's, all you cells will be rendered as check boxes thanks to you implementation of getColumnClass(). All user input to these check boxes is collected with our setValueAt().