Preventing checkboxes from moving in a JTable - java

I have a column in a JTable with cells rendered as checkboxes below:
How do I prevent the checkboxes from moving every time I click on it? Here is my cell renderer and cell editor:
protected class CheckBoxCellRenderer extends JCheckBox implements TableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
this.setSelected((Boolean) tableModel.getValueAt(row, 0));
return this;
}
}
protected class CheckBoxCellEditor extends AbstractCellEditor implements TableCellEditor, ActionListener {
private final CheckBoxCellRenderer cell = new CheckBoxCellRenderer();
private int row;
public CheckBoxCellEditor() {
cell.addActionListener(this);
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int rowIndex, int vColIndex) {
cell.setSelected(((Boolean) value).booleanValue());
row = rowIndex;
return cell;
}
#Override
public Object getCellEditorValue() {
return cell.isSelected();
}
}
Thanks!

Don't create a custom renderer/editor.
JTable already supports a default renderer. Just override the getColumnClass(...) method of the TableModel or JTable to return Boolean.class for the first column and the JTable will use the default renderer/editor.

Related

Buttons in table cell can't be pressed

I'm using Java Swing to make a table with buttons I can use to be actions based on the row.
I am using a custom cell renderer to try rendering multiple buttons in a table cell. I have managed to cobble together something that does what I want visually but the buttons don't actually function. They simply don't fire and don't react visually to clicks or mouse overs. Looking for a way to make the buttons react and actually fire their actions.
Cell renderer class:
public class ButtonsCell extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {
JPanel panel;
public ButtonsCell() {
this.updateData(Collections.emptyList());
}
private void updateData(List<JButton> buttons) {
this.panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
for(JButton button : buttons) {
button.setMargin(new Insets(-2, 0, -2, 0));
this.panel.add(button);
}
}
private void updateData(List<JButton> buttons, boolean isSelected, JTable table) {
this.updateData(buttons);
if (isSelected) {
this.panel.setBackground(table.getSelectionBackground());
}else{
this.panel.setBackground(table.getBackground());
}
}
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
this.updateData((List<JButton>)value, isSelected, table);
return panel;
}
public Object getCellEditorValue() {
return null;
}
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
this.updateData((List<JButton>)value, isSelected, table);
return panel;
}
}
Visually, this produces:

Get JTable default TableCellRenderer and Manipulate it?

I am going to be using a lot of custom TableCellRenderers in a JTable, but I don't want to have to recreate the default properties for every single one, because it seems I have to start with a plain JLabel. This creates a ton of annoying overhead, because I even have to populate the value myself as well as the matching background, the foreground, the font, etc... to match the rest of the JTable.
Is there a way I can retrieve the parent TableCellRenderer or the it's resulting JLabel and manipulate that? That way I have all the defaults set already, and I can just manipulate the properties I am actually changing? I have tried everything with super.getCellRenderer and it is not giving me anything to accomplish that.
Also, my TableCellRenderer is not column-specific. Each cell can vary.
public class ActionTable extends JTable {
...
public TableCellRenderer getCellRenderer(int row, int column) {
String colName = ((ActionTableModel)this.getModel()).getColumnName(column);
if ( colName.equals("CUSTOM COL") ) {
return new ActionCellRenderer(this);
}
return super.getCellRenderer(this.convertRowIndexToModel(row), this.convertColumnIndexToModel(column));
}
public class ActionCellRenderer extends JLabel implements TableCellRenderer {
private ActionTable actionTable;
public ActionCellRenderer(ActionTable actionTable) {
this.actionTable = actionTable;
setOpaque(true); //MUST do this for background to show up.
}
#Override
public Component getTableCellRendererComponent(JTable table, Object color,boolean isSelected, boolean hasFocus,int row, int column) {
int modelRow = actionTable.convertRowIndexToModel(row);
int modelCol = actionTable.convertColumnIndexToModel(column);
String colName = table.getModel().getColumnName(modelCol);
/*
annoying overhead to retrieve default cell renderer properties
to match the rest of the JTable
*/
if (isSelected)
{
setBackground(table.getSelectionBackground());
setForeground(table.getSelectionForeground());
}
else
{
setBackground(table.getBackground());
setForeground(table.getForeground());
}
//AND I HAVE TO RETRIEVE THE VALUE MYSELF TOO
String textVal = ((ActionTableModel)table.getModel()).getValueAt(modelRow, modelCol).toString();
this.setHorizontalAlignment(SwingConstants.CENTER);
this.setFont(new Font("Serif", Font.PLAIN, 15));
this.setText(textVal);
//NOW I CAN DO CUSTOMIZATIONS
//PUT CUSTOMIZATIONS HERE
return this;
}
Just have your ActionCellRenderer extend DefaultTableCellRenderer and call the getTableCellRendererComponent() method to get the JLabel:
public class ActionCellRenderer extends DefaultTableCellRenderer{
public Component getTableCellRendererComponent(JTable table, Object value,boolean isSelected, boolean hasFocus,int row, int column){
//get the label
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value,isSelected, hasFocus,row, column);
//do whatever you want with the label
return label;
}
}

JTable render Boolean as JToggleButton

Hi I am able to render the Boolean column as a JToggleButton but if I keep the button pressed, I am seeing the checkbox instead of the button.
TableColumnModel tcm = smartAlertsTable.getColumnModel();
TableColumn tc = tcm.getColumn( Index of the boolean column);
tc.setCellRenderer(new ActiveAlertRenderer());
where ActiveAlertRenderer is
public class ActiveAlertRenderer extends JToggleButton implements
TableCellRenderer
{
public ActiveAlertRenderer()
{
super();
}
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column)
{
boolean isActive = ((Boolean) value).booleanValue();
if (isActive)
{
this.setText("Armed");
this.setSelected(false);
value = Boolean.TRUE;
}
else
{
this.setText("Triggered");
this.setSelected(true);
value = Boolean.FALSE;
}
return this;
}
}
How do I prevent the checkbox from appearing?
JTable uses a checkbox as a default renderer and editor for boolean columns. You provided your own renderer but the editor remains default. This is the checkbox that you see when you edit the cell. You'd have to provide a custom editor (implementation of TableCellEditor) in a similar fashion you did the renderer. You can set it up using JTable.setDefaultEditor() or TableColumn.setCellRenderer.
See Concepts: Editors and Renderers in How to Use Tables tutorial for more details.
Try like this:
public class ActiveAlertRenderer extends DefaultTableRenderer {
private JToggleButton toggleButton = new JToggleButton();
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (column==INDEX_OF_TOGGLE_BUTTON_COLUMN) {
boolean active = (Boolean) argValue;
if (active) {
toggleButton.setText("Armed");
} else {
toggleButton.setText("Triggered");
}
toggleButton.setSelected(active);
return toggleButton;
}
else {
return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
}
}

JTable cell with two data types

I have a column in JTable which should display two types: String OR ImageIcon, not both. Each cell in that column has own thread which calculates data. In the beginning I put to each cell an image(like waiting logo), then REPLACE(not append) the image with a string of calculated data. I tried to extend default TableCell renderer, but it displays image like object address(javax.swing.ImageIcon#342...) and then replaces with string. Another variant, it displays the image correctly, but replaces it with empty string(or it is not visible?).
How to set it up so the table displays cell content correctly according to type?
Here is what I have at the moment:
class IconAndStringRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 3606788739290618405L;
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
if (value instanceof Icon) {
setIcon((Icon) value);
setText("");
}
return this;
}
}
Here is JTable:
table = new JTable(model) {
private static final long serialVersionUID = 8058795799817761161L;
public Class<?> getColumnClass(int column) {
if (column == TARGET_COLUMN)
return ImageIcon.class;
else
return super.getColumnClass(column);
}
};
A few more questions:
How to set it so the text replaces the image, not write text after image(even if it's not visible);
How to set text color, I gonna use setForebackground(Color c), but if I use it, the image is not dislayed.
Is it possible to make it working with Jlabel? Set up required Jlabel(with image or text) in the thread which modifies a cell and just setValueAt(label, row, column);
You need a custom renderer which can understand both types you use.
For example
public class IconAndStringRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
if (value instanceof Icon) {
setText("");
setIcon((Icon) value);
}
return this;
}
}
Try to set this class as your column renderer
The default renderer knows how to display both strings and icons. All you have to do is ensure that your TableModel returns the correct class from getColumnClass(), String.class and Icon.class, respectively. Examples may be found here.
Addendum: Here's a minimal example to illustrate the principle, based on default implementations.
Addendum: Not sure if it works when needed to return different classes for the same column.
If you really need to choose the renderer on a per-cell basis, override prepareRenderer(), as shown here.
import java.awt.EventQueue;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;
/** #see https://stackoverflow.com/a/14672312/230513 */
public class Test {
private static final Icon YES = UIManager.getIcon("InternalFrame.maximizeIcon");
private static final Icon NO = UIManager.getIcon("InternalFrame.closeIcon");
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DefaultTableModel model = new DefaultTableModel(
new Object[]{"Name", "Icon"}, 0) {
#Override
public Class<?> getColumnClass(int col) {
if (col == 1) {
return Icon.class;
} else {
return super.getColumnClass(col);
}
}
};
model.addRow(new Object[]{"One", YES});
model.addRow(new Object[]{"Two", NO});
final JTable table = new JTable(model);
table.setRowHeight(YES.getIconHeight() +2);
f.add(table);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test().display();
}
});
}
}
To take advantage of the default renderers you can override the getCellRenderer(...) method of JTable to return the appropriate renderer. Something like:
public TableCellRenderer getCellRenderer(int row, int column)
{
int modelColumn = convertColumnIndexToModel(column);
if (modelColumn == ???)
{
Class rowClass = getModel().getValueAt(row, modelColumn).getClass();
return getDefaultRenderer( rowClass );
}
else
return super.getCellRenderer(row, column);
}

How to use JLists in JTable cells?

I would like a simple way to put a JList in a column of a JTable. I already have the JLists and the table, but when put in the table, the Jlists are displayed as Strings, which is normal, because I use DefaultTableModel. I have overriden the getColumnClass() as:
public Class<? extends Object> getColumnClass(int c)
{
return getValueAt(0, c).getClass();
}
but this just formats the integer and float values.
I suppose that setValueAt() and getValueAt() should also be overriden, in order to return am array of Strings when I call JList.getSelectedValues(), but I can't figure out how.
I also want the cells to be editable, so the users can choose one or more option from the JList. After editing a row, I use a Save button to save the changes in a database, so I don't think I need a ListSelectionListener, JList.getSelectedValues() works just fine.
I know this is a common question, but I couldn't find an answer here. If this is a duplicate, please let me know and I will delete it.
I've done it. For everyone who needs the same thing, here is what I've done:
1)I have created a JScrollTableRenderer, and set the column I needed to show the JList to use this renderer
table.getColumnModel().getColumn(5).setCellRenderer(new JScrollTableRenderer());
The JScrollTableRenderer class content:
public class JScrollTableRenderer extends DefaultTableCellRenderer {
JScrollPane pane = new JScrollPane();
public JScrollTableRenderer()
{
super();
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column)
{
pane = (JScrollPane) value;
return pane;
}
}
2)I have created a JScrollTableEditor, and set the column I needed to show the JList to use this editor
table.getColumnModel().getColumn(5).setCellEditor(new JScrollTableEditor());
The JScrollTableEditor class content:
public class JScrollTableEditor extends AbstractCellEditor implements TableCellEditor {
JScrollPane component = new JScrollPane();
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,
int rowIndex, int vColIndex)
{
component = ((JScrollPane) value);
return ((JScrollPane) value);
}
public Object getCellEditorValue()
{
return component;
}
}
3)I added this method in the JTable model:
public Class<? extends Object> getColumnClass(int c)
{
if(c == 5) return JScrollPane.class;
else return getValueAt(0, c).getClass();
}

Categories