Buttons in table cell can't be pressed - java

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:

Related

TableCellRenderer sets color to many cells and not just one

I have a JTable, that I want I want to be able to change the color of a single cell that is clicked on.
Here is a simplified version of my code:
public class TableFrame extends JFrame {
public TableFrame() {
JTable table = new JTable(8, 8);
table.setGridColor(Color.BLACK);
table.setDefaultRenderer(CustomCellRenderer.class, new CustomCellRenderer());
getContentPane().add(table);
}
public class CustomCellRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (hasFocus) {
l.setBackground(Color.red);
l.setText("Hello");
}
return l;
}
}
}
When I click a certain cell, I expect it to change color to red and add "Hello" to it. It changes the text, but for some weird reason, it changes the color of all the cells after it? And when I click an uncolored cell, it does the same, but not always in an organised way if that makes sense? Like, it won't color all the cells after it, but maybe some that are just above and leave others blank..
It's really weird and makes no sense whatsoever. What is happening??
Having dug around the DefaultTableCellRenderer class a bit, when you call setBackground on the JLabel component, which is backing the DefaultTableCellRenderer, it is storing the value you use...
public void setBackground(Color c) {
super.setBackground(c);
unselectedBackground = c;
}
When the cell is painted again, it's this value (unselectedBackground) which is been used to repaint the cell in "default" mode...
if (isSelected) {
//...
} else {
Color background = unselectedBackground != null
? unselectedBackground
: table.getBackground();
if (background == null || background instanceof javax.swing.plaf.UIResource) {
Color alternateColor = DefaultLookup.getColor(this, ui, "Table.alternateRowColor");
if (alternateColor != null && row % 2 != 0) {
background = alternateColor;
}
}
super.setForeground(unselectedForeground != null
? unselectedForeground
: table.getForeground());
super.setBackground(background);
}
This means, the moment you use setBackground and pass it Color.RED, the DefaultTableCellRenderer assumes that this becomes the default color for ALL unselected cells.
The only choice you have is to reset the background color manually, for example...
public class CustomCellRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (hasFocus) {
l.setBackground(Color.red);
l.setText("Hello");
} else if (!isSelected) {
l.setBackground(table.getBackground());
}
return l;
}
}
Also, you should really be using something more like...
table.setDefaultRenderer(Object.class, new CustomCellRenderer());
to register the cell renderer, as it's the Class type returned by TableModel#getColumnClass which determines which cell renderer is used ;)
Since the OP only wants help with the rendered and not with the data... here goes (assuming there is a function called hasBeenClicked(row,column) method available to determine whether the cell has been visited yet.
public class CustomCellRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (hasBeenClicked(row,column)) {
l.setBackground(Color.red);
l.setText("Hello");
} else {
// reset the label to white background
l.setBackground(Color.white);
l.setText("Hello");
}
return l;
}
}
}
Also note that the registration of the renderer should be
table.setDefaultRenderer(Object.class, new CustomCellRenderer());
Since we want all columns to have this renderer (renderers are registered against the class of the column in the model).
I tested with the below as the hasBeenClicked method.
public boolean hasBeenClicked(int row, int column){
return (row%2==0 && column%2==0);
}
Just implement your own tracking of whether a cell has been clicked or not and you should be good to go. Remember that you should not use the renderer to track the clicks, use some kind of listener instead.

Is it possible to detect a button clicked inside a table cell?

I a swing application in which i have a table inside it i'm putting a panel that can contain a button.The code is follow
public class MyCellDataRenderer implements TableCellRenderer, TableCellEditor {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
MyCellData myCellData = (MyCellData) table.getValueAt(row, column);
JPanel panel = GridBagHelper.createPanel();
if (myCellData.isATableHeader()) {
panel.setBackground(myCellData.getCellBackgroundColor());
panel.add(myCellData.getContenant(), GridBagHelper.createGridBagConstraints(0, 0, 1, 1,
GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH));
return panel;
}
boolean condition=true;
if (condition==true) {
panel.setBackground(myCellData.getCellBackgroundColor());
panel.add(myCellData.getContenant(), GridBagHelper.createGridBagConstraints(0, 0, 1, 1,
GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH));
return panel;
}
panel.setBackground(myCellData.getCellBackgroundColor());
panel.add(myCellData.getContenant(), GridBagHelper.createGridBagConstraints(0, 0, 1, 1,
GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH));
return panel;
}
My question is can i detect a click on a button that contains inside the panel or not?
I'm asking about if it's possible technically or not?
Thanks
In my cell, I have two buttons and three labels; they are all in one panel.
You are correct to use a TableCellRenderer and TableCellEditor. In this complete example, the StatusEditor queries the enclosing StatusPanel and returns a suitable value in its implementation of getCellEditorValue().
Yes it is possible and there are few ways how to do it based on your application design. As I do not know the details I would suggest this simple solution:
table.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
final int row = table.rowAtPoint(e.getPoint());
final int column = table.columnAtPoint(e.getPoint());
if(isButtonCell(row, column)) { //check if the cell is your button
handleButtonClicked(); //invoke the click button handle
}
}
});

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);
}
}
}

Preventing checkboxes from moving in a JTable

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.

Categories