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

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

Related

Multiple buttons in one table column in Java Swing won't perform action

What I need is to dynamically add buttons to one JTable column. Each row can have 0-N buttons in this column. I figured how to add these buttons but after clicking on them nothing happens, despite each button has action listener specified.
In addition, right after I create these buttons I need to set ActionListeners to it, so that when this button is clicked, some action will be done (action outside of TasksCell class).
Here is how I set renderer and editor and how I create buttons :
table.setDefaultRenderer(Tasks.class, new TasksCell());
table.setDefaultEditor(Tasks.class, new TasksCell());
//
//
//
ActionListener listener = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
// DOING STH
}
};
JButton button = new JButton("task");
button.addActionListener(listener);
// tableModel is custom TableModel
tableModel.addNewTask(button);
This is how I add task(button) to my class for storing buttons in my custom table model:
// I need to calculate where to add button, row is index of the row in table
((Tasks)this.getValueAt(row, 3)).addTask(task);
Then this is my class for storing buttons :
public class Tasks {
private List<JButton> taskButtons = new ArrayList<>();
public void addTask(JButton task) {
taskButtons.add(task);
}
public List<JButton> getTaskButtons() {
return taskButtons;
}
public void setTaskButtons(List<JButton> tasks) {
this.taskButtons = tasks;
}
}
And finally class with table cell editor and renderer :
public class TasksCell extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {
#Override
public Object getCellEditorValue() {
return null;
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
Tasks tasks = (Tasks)value;
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
tasks.getTaskButtons().forEach(task -> {
panel.add(task);
});
panel.setBackground(table.getSelectionBackground());
return panel;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Tasks tasks = (Tasks)value;
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
tasks.getTaskButtons().forEach(button -> {
panel.add(button);
});
panel.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
return panel;
}
}
As I mentioned before, buttons are added to panel in table correctly

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:

How to add JCheckBox beside a String in JList and allows it to select and open multiple files stored in a folder without the use of ctrl key? [duplicate]

What would be the best way to have a list of items with a checkbox each in Java Swing?
I.e. a JList with items that have some text and a checkbox each?
A wonderful answer is this CheckBoxList. It implements Telcontar's answer (though 3 years before :)... I'm using it in Java 1.6 with no problems. I've also added an addCheckbox method like this (surely could be shorter, haven't used Java in a while):
public void addCheckbox(JCheckBox checkBox) {
ListModel currentList = this.getModel();
JCheckBox[] newList = new JCheckBox[currentList.getSize() + 1];
for (int i = 0; i < currentList.getSize(); i++) {
newList[i] = (JCheckBox) currentList.getElementAt(i);
}
newList[newList.length - 1] = checkBox;
setListData(newList);
}
I tried out the demo for the Jidesoft stuff, playing with the CheckBoxList I encountered some problems (behaviors that didn't work). I'll modify this answer if I find problems with the CheckBoxList I linked to.
Create a custom ListCellRenderer and asign it to the JList.
This custom ListCellRenderer must return a JCheckbox in the implementantion of getListCellRendererComponent(...) method.
But this JCheckbox will not be editable, is a simple paint in the screen is up to you to choose when this JCheckbox must be 'ticked' or not,
For example, show it ticked when the row is selected (parameter isSelected), but this way the check status will no be mantained if the selection changes. Its better to show it checked consulting the data below the ListModel, but then is up to you to implement the method who changes the check status of the data, and notify the change to the JList to be repainted.
I Will post sample code later if you need it
ListCellRenderer
Just implement a ListCellRenderer
public class CheckboxListCellRenderer extends JCheckBox implements ListCellRenderer {
public Component getListCellRendererComponent(JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
setComponentOrientation(list.getComponentOrientation());
setFont(list.getFont());
setBackground(list.getBackground());
setForeground(list.getForeground());
setSelected(isSelected);
setEnabled(list.isEnabled());
setText(value == null ? "" : value.toString());
return this;
}
}
and set the renderer
JList list = new JList();
list.setCellRenderer(new CheckboxListCellRenderer());
this will result in
Details at Custom swing component renderers.
PS: If you want radio elements just replace extends JCheckbox with extends JRadioButton.
I'd probably be looking to use a JTable rather than a JList and since the default rendering of a checkbox is rather ugly, I'd probably be looking to drop in a custom TableModel, CellRenderer and CellEditor to represent a boolean value. Of course, I would imagine this has been done a bajillion times already. Sun has good examples.
Better solution for Java 7 and newer
I stumbled upon this question and realized that some of the answers are pretty old and outdated. Nowadays, JList is generic and thus there are better solutions.
My solution of the generic JCheckBoxList:
import java.awt.Component;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.event.*;
#SuppressWarnings("serial")
public class JCheckBoxList extends JList<JCheckBox> {
protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
public JCheckBoxList() {
setCellRenderer(new CellRenderer());
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int index = locationToIndex(e.getPoint());
if (index != -1) {
JCheckBox checkbox = (JCheckBox) getModel().getElementAt(index);
checkbox.setSelected(!checkbox.isSelected());
repaint();
}
}
});
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
public JCheckBoxList(ListModel<JCheckBox> model){
this();
setModel(model);
}
protected class CellRenderer implements ListCellRenderer<JCheckBox> {
public Component getListCellRendererComponent(
JList<? extends JCheckBox> list, JCheckBox value, int index,
boolean isSelected, boolean cellHasFocus) {
JCheckBox checkbox = value;
//Drawing checkbox, change the appearance here
checkbox.setBackground(isSelected ? getSelectionBackground()
: getBackground());
checkbox.setForeground(isSelected ? getSelectionForeground()
: getForeground());
checkbox.setEnabled(isEnabled());
checkbox.setFont(getFont());
checkbox.setFocusPainted(false);
checkbox.setBorderPainted(true);
checkbox.setBorder(isSelected ? UIManager
.getBorder("List.focusCellHighlightBorder") : noFocusBorder);
return checkbox;
}
}
}
For dynamically adding JCheckBox lists you need to create your own ListModel or add the DefaultListModel.
DefaultListModel<JCheckBox> model = new DefaultListModel<JCheckBox>();
JCheckBoxList checkBoxList = new JCheckBoxList(model);
The DefaultListModel are generic and thus you can use methods specified by JAVA 7 API here like this:
model.addElement(new JCheckBox("Checkbox1"));
model.addElement(new JCheckBox("Checkbox2"));
model.addElement(new JCheckBox("Checkbox3"));
I recommend you use a JPanel with a GridLayout of 1 column. Add the checkBoxes to the JPanel, and set the JPanel as the data source of a JScrollPane. And to get the selected CheckBoxes, just call the getComponents() of the JPanel to get the CheckBoxes.
Odds are good w/ Java that someone has already implemented the widget or utility you need. Part of the benefits of a large OSS community. No need to reinvent the wheel unless you really want to do it yourself. In this case it would be a good learning exercise in CellRenderers and Editors.
My project has had great success with JIDE. The component you want, a Check Box List, is in the JIDE Common Layer (which is OSS and hosted on java.net). The commercial stuff is good too, but you don't need it.
http://www.jidesoft.com/products/oss.htm
https://jide-oss.dev.java.net/
I don't like the solutions that put a Checkbox into the model. The model should only contain data not display elements.
I found this http://www.java2s.com/Tutorials/Java/Swing_How_to/JList/Create_JList_of_CheckBox.htm
which I optimized a bit. The ACTIVE flag represents the Checkbox, the SELECTED flag shows what entry the cursor sits on.
my version requires a renderer
import java.awt.Component;
import javax.swing.JCheckBox;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
class CheckListRenderer extends JCheckBox implements ListCellRenderer<Entity> {
#Override
public Component getListCellRendererComponent(JList<? extends Entity> list,
Entity value, int index, boolean isSelected, boolean cellHasFocus) {
setEnabled(list.isEnabled());
setSelected(value.isActive()); // sets the checkbox
setFont(list.getFont());
if (isSelected) { // highlights the currently selected entry
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
setText(value.toString()+" - A" + value.isActive()+" - F"+cellHasFocus+" - S"+isSelected );
return this;
}
}
and an entity that got the active field:
public class Entity {
private boolean active = true;
public boolean isActive() {
return active;
}
public void setActive(boolean isActive) {
this.active = isActive;
}
}
Now you only have to add this to your JList:
list = new JList<Entity>();
list.setModel(new DefaultListModel<Entity>());
list.setCellRenderer(new CheckListRenderer());
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent event) {
if (event.getX() < 20) {
// Quick and dirty: only change the tick if clicked into the leftmost pixels
#SuppressWarnings("unchecked")
JList<Entity> list = ((JList<Entity>) event.getSource());
int index = list.locationToIndex(event.getPoint());// Get index of item clicked
if (index >= 0) {
Entity item = (Entity) list.getModel().getElementAt(index);
item.setActive(!item.isActive()); // Toggle selected state
list.repaint(list.getCellBounds(index, index));// Repaint cell
}
}
}
});
All of the aggregate components in Swing--that is, components made up other components, such as JTable, JTree, or JComboBox--can be highly customized. For example, a JTable component normally displays a grid of JLabel components, but it can also display JButtons, JTextFields, or even other JTables. Getting these aggregate components to display non-default objects is the easy part, however. Making them respond properly to keyboard and mouse events is a much harder task, due to Swing's separation of components into "renderers" and "editors." This separation was (in my opinion) a poor design choice and only serves to complicate matters when trying to extend Swing components.
To see what I mean, try enhancing Swing's JList component so that it displays checkboxes instead of labels. According to Swing philosophy, this task requires implementing two interfaces: ListCellRenderer (for drawing the checkboxes) and CellEditor (for handling keyboard and mouse events on the checkboxes). Implementing the ListCellRenderer interface is easy enough, but the CellEditor interface can be rather clumsy and hard to understand. In this particular case, I would suggest forgetting CellEditor entirely and to handle input events directly, as shown in the following code.
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
public class CheckBoxList extends JList
{
protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
public CheckBoxList()
{
setCellRenderer(new CellRenderer());
addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
int index = locationToIndex(e.getPoint());
if (index != -1) {
JCheckBox checkbox = (JCheckBox)
getModel().getElementAt(index);
checkbox.setSelected(
!checkbox.isSelected());
repaint();
}
}
}
);
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
protected class CellRenderer implements ListCellRenderer
{
public Component getListCellRendererComponent(
JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus)
{
JCheckBox checkbox = (JCheckBox) value;
checkbox.setBackground(isSelected ?
getSelectionBackground() : getBackground());
checkbox.setForeground(isSelected ?
getSelectionForeground() : getForeground());
checkbox.setEnabled(isEnabled());
checkbox.setFont(getFont());
checkbox.setFocusPainted(false);
checkbox.setBorderPainted(true);
checkbox.setBorder(isSelected ?
UIManager.getBorder(
"List.focusCellHighlightBorder") : noFocusBorder);
return checkbox;
}
}
}
Here, I intercept mouse clicks from the listbox and simulate a click on the appropriate checkbox. The result is a "CheckBoxList" component that is both simpler and smaller than an equivalent component using the CellEditor interface. To use the class, simply instantiate it, then pass it an array of JCheckBox objects (or subclasses of JCheckBox objects) by calling setListData. Note that the checkboxes in this component will not respond to keypresses (i.e. the spacebar), but you could always add your own key listener if needed.
Source: DevX.com
Here is just a little addition to the JCheckBoxList by Rawa. This will add the ability to select using space bar. If multiple items are selected, all will be set to inverted value of the first item.
addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
int index = getSelectedIndex();
if (index != -1 && e.getKeyCode() == KeyEvent.VK_SPACE) {
boolean newVal = !((JCheckBox) (getModel()
.getElementAt(index))).isSelected();
for (int i : getSelectedIndices()) {
JCheckBox checkbox = (JCheckBox) getModel()
.getElementAt(i);
checkbox.setSelected(newVal);
repaint();
}
}
}
});
this is yet another example of making list with checkboxes
class JCheckList<T> extends JList<T> {
protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
public void setSelected(int index) {
if (index != -1) {
JCheckBox checkbox = (JCheckBox) getModel().getElementAt(index);
checkbox.setSelected(
!checkbox.isSelected());
repaint();
}
}
protected static class CellListener
extends DefaultListModel
implements ListDataListener {
ListModel ls;
public CellListener(ListModel ls) {
ls.addListDataListener(this);
int i = ls.getSize();
for (int v = 0; v < i; v++) {
var r = new JCheckBox();
r.setText(ls.getElementAt(v).toString());
this.addElement(r);
}
this.ls = ls;
}
#Override
public void intervalAdded(ListDataEvent e) {
int begin = e.getIndex0();
int end = e.getIndex1();
for (; begin <= end; begin++) {
var r = new JCheckBox();
r.setText(ls.getElementAt(begin).toString());
this.add(begin, r);
}
}
#Override
public void intervalRemoved(ListDataEvent e) {
int begin = e.getIndex0();
int end = e.getIndex1();
for (; begin <= end; end--) {
this.remove(begin);
}
}
#Override
public void contentsChanged(ListDataEvent e) {
}
}
public JCheckList() {
setCellRenderer(new CellRenderer());
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int index = locationToIndex(e.getPoint());
setSelected(index);
}
}
);
addKeyListener(new KeyListener(){
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE){
int index = JCheckList.this.getSelectedIndex();
setSelected(index);
}
}
#Override
public void keyReleased(KeyEvent e) {
}
});
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
#Override
public void setModel(ListModel<T> d) {
var r = new CellListener(d);
d.addListDataListener(r);
super.setModel(r);
}
protected class CellRenderer implements ListCellRenderer {
public Component getListCellRendererComponent(
JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
JCheckBox checkbox = (JCheckBox) value;
checkbox.setBackground(isSelected
? getSelectionBackground() : getBackground());
checkbox.setForeground(isSelected
? getSelectionForeground() : getForeground());
checkbox.setEnabled(isEnabled());
checkbox.setFont(getFont());
checkbox.setFocusPainted(false);
checkbox.setBorderPainted(true);
checkbox.setBorder(isSelected
? UIManager.getBorder(
"List.focusCellHighlightBorder") : noFocusBorder);
return checkbox;
}
}
}

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

ActionListener on JButton in JTable header freezes application

I have a JTable which uses a JButton for one of the headers on a column.
Without an ActionListener the button seems to function normally, I can see it visually click. However when I add in an ActionListener which should pop up a JOptionPane the application stops redrawing itself, stops responding to any actions, and doesn't draw the option pane.
public class ButtonHeaderRenderer extends JButton implements TableCellRenderer, ActionListener
{
int pushedColumn;
public ButtonHeaderRenderer(Icon image, JTableHeader header, ActionListener actionListener)
{
pushedColumn = -1;
setIcon(image);
setForeground(header.getForeground());
setBackground(header.getBackground());
setBorder(UIManager.getBorder("TableHeader.cellBorder"));
setMargin(new Insets(0, 0, 0, 0));
addActionListener(this);
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
setText((value == null) ? "" : value.toString());
boolean isPressed = (column == pushedColumn);
getModel().setPressed(isPressed);
getModel().setArmed(isPressed);
return this;
}
public void setPressedColumn(int col)
{
this.pushedColumn = col;
}
public void actionPerformed(ActionEvent e)
{
JOptionPane.showMessageDialog(null, "alert", "alert", JOptionPane.ERROR_MESSAGE);
}
}
Does anyone know what might be causing the issue?
edit: It seems that creating a new thread in the actionPerformed method and creating the JOptionPane in that works. This seems like a hack however, I've used JOptionPane in other places and it works fine without starting a new thread.
Try to call the JOptionPane inside SwingUtilities.invokeLater()

Categories