I have a JTable where I have inserted two buttons (delete, search) in each row.
For each button I specify the proper action in my constructor like this:
Action search = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTable table = (JTable) e.getSource();
int modelRow = Integer.valueOf(e.getActionCommand());
System.out.println("Search action for row: " + modelRow);
// do some processing here
// tb.searchMore(modelRow);
}
};
And then I specify to which column the button should be created like this:
// column 4 of JTable table should implement action 'search'
ButtonColumn searchButtonColumn = new ButtonColumn(table, search, 4);
searchButtonColumn.setMnemonic(KeyEvent.VK_D);
And I have a proper Button Renderer Class.
The delete button (which is being created and initialized exactly the same way) works just fine. So does a similar save button I created for another table.
The problem is with the search button. It creates the graphic button in the table, but its action is never actualluy being called. I noticed that this happens when I place it in a column other that the very first one. Any ideas for this strange behaviour? The column I am placing it is not out of bounds. Is there a problem with placing two buttons in tha same row?
Below is the Button Renderer code:
public class ButtonColumn extends AbstractCellEditor
implements TableCellRenderer, TableCellEditor, ActionListener, MouseListener {
private JTable table;
private Action action;
private int mnemonic;
private Border originalBorder;
private Border focusBorder;
private JButton renderButton;
private JButton editButton;
private Object editorValue;
private boolean isButtonColumnEditor;
/**
* Create the ButtonColumn to be used as a renderer and editor. The
* renderer and editor will automatically be installed on the TableColumn
* of the specified column.
*
* #param table the table containing the button renderer/editor
* #param action the Action to be invoked when the button is invoked
* #param column the column to which the button renderer/editor is added
*/
public ButtonColumn(JTable table, Action action, int column) {
this.table = table;
this.action = action;
renderButton = new JButton();
editButton = new JButton();
editButton.setFocusPainted(false);
editButton.addActionListener(this);
originalBorder = editButton.getBorder();
setFocusBorder(new LineBorder(Color.BLUE));
TableColumnModel columnModel = table.getColumnModel();
columnModel.getColumn(column).setCellRenderer(this);
columnModel.getColumn(column).setCellEditor(this);
table.addMouseListener(this);
}
/**
* Get foreground color of the button when the cell has focus
*
* #return the foreground color
*/
public Border getFocusBorder() {
return focusBorder;
}
/**
* The foreground color of the button when the cell has focus
*
* #param focusBorder the foreground color
*/
public void setFocusBorder(Border focusBorder) {
this.focusBorder = focusBorder;
editButton.setBorder(focusBorder);
}
public int getMnemonic() {
return mnemonic;
}
/**
* The mnemonic to activate the button when the cell has focus
*
* #param mnemonic the mnemonic
*/
public void setMnemonic(int mnemonic) {
this.mnemonic = mnemonic;
renderButton.setMnemonic(mnemonic);
editButton.setMnemonic(mnemonic);
}
#Override
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column) {
if (value == null) {
editButton.setText("");
editButton.setIcon(null);
} else if (value instanceof Icon) {
editButton.setText("");
editButton.setIcon((Icon) value);
} else {
editButton.setText(value.toString());
editButton.setIcon(null);
}
this.editorValue = value;
return editButton;
}
#Override
public Object getCellEditorValue() {
return editorValue;
}
//
// Implement TableCellRenderer interface
//
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (isSelected) {
renderButton.setForeground(table.getSelectionForeground());
renderButton.setBackground(table.getSelectionBackground());
} else {
renderButton.setForeground(table.getForeground());
renderButton.setBackground(UIManager.getColor("Button.background"));
}
if (hasFocus) {
renderButton.setBorder(focusBorder);
} else {
renderButton.setBorder(originalBorder);
}
// renderButton.setText( (value == null) ? "" : value.toString() );
if (value == null) {
renderButton.setText("");
renderButton.setIcon(null);
} else if (value instanceof Icon) {
renderButton.setText("");
renderButton.setIcon((Icon) value);
} else {
renderButton.setText(value.toString());
renderButton.setIcon(null);
}
return renderButton;
}
//
// Implement ActionListener interface
//
/*
* The button has been pressed. Stop editing and invoke the custom Action
*/
public void actionPerformed(ActionEvent e) {
int row = table.convertRowIndexToModel(table.getEditingRow());
fireEditingStopped();
// Invoke the Action
ActionEvent event = new ActionEvent(table,
ActionEvent.ACTION_PERFORMED, "" + row);
action.actionPerformed(event);
}
//
// Implement MouseListener interface
//
/*
* When the mouse is pressed the editor is invoked. If you then then drag
* the mouse to another cell before releasing it, the editor is still
* active. Make sure editing is stopped when the mouse is released.
*/
public void mousePressed(MouseEvent e) {
if (table.isEditing()
&& table.getCellEditor() == this) {
isButtonColumnEditor = true;
}
}
public void mouseReleased(MouseEvent e) {
if (isButtonColumnEditor
&& table.isEditing()) {
table.getCellEditor().stopCellEditing();
}
isButtonColumnEditor = false;
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
}
I guess you forgot to set the action command to the rendered button. You rely on that command in your action. Try something like
editButton.setActionCommand(String.valueOf(column));
in your cell renderer.
Related
I've been trying to put a clickable button in a JTable, and I'm not doing great.
Im using the ButtonColumn class to do so, but the actions are ignored - the buttons are showing but nothing is happening on click.
/**
* The ButtonColumn class provides a renderer and an editor that looks like a
* JButton. The renderer and editor will then be used for a specified column
* in the table. The TableModel will contain the String to be displayed on
* the button.
*
* The button can be invoked by a mouse click or by pressing the space bar
* when the cell has focus. Optionally a mnemonic can be set to invoke the
* button. When the button is invoked the provided Action is invoked. The
* source of the Action will be the table. The action command will contain
* the model row number of the button that was clicked.
*
*/
public class ButtonColumn extends AbstractCellEditor
implements TableCellRenderer, TableCellEditor, ActionListener, MouseListener{
/**
*
*/
private static final long serialVersionUID = -6737467999355167233L;
private JTable table;
private Action action;
private int mnemonic;
private Border originalBorder;
private Border focusBorder;
private JButton renderButton;
private JButton editButton;
private Object editorValue;
private boolean isButtonColumnEditor;
/**
* Create the ButtonColumn to be used as a renderer and editor. The
* renderer and editor will automatically be installed on the TableColumn
* of the specified column.
*
* #param table the table containing the button renderer/editor
* #param action the Action to be invoked when the button is invoked
* #param column the column to which the button renderer/editor is added
*/
public ButtonColumn(JTable table, Action action, int column)
{
this.table = table;
this.action = action;
renderButton = new JButton();
editButton = new JButton();
editButton.setFocusPainted( false );
editButton.addActionListener( this );
originalBorder = editButton.getBorder();
setFocusBorder( new LineBorder(Color.BLUE) );
TableColumnModel columnModel = table.getColumnModel();
columnModel.getColumn(column).setCellRenderer( this );
columnModel.getColumn(column).setCellEditor( this );
table.addMouseListener( this );
}
/**
* Get foreground color of the button when the cell has focus
*
* #return the foreground color
*/
public Border getFocusBorder()
{
return focusBorder;
}
/**
* The foreground color of the button when the cell has focus
*
* #param focusBorder the foreground color
*/
public void setFocusBorder(Border focusBorder)
{
this.focusBorder = focusBorder;
editButton.setBorder( focusBorder );
}
public int getMnemonic()
{
return mnemonic;
}
/**
* The mnemonic to activate the button when the cell has focus
*
* #param mnemonic the mnemonic
*/
public void setMnemonic(int mnemonic)
{
this.mnemonic = mnemonic;
renderButton.setMnemonic(mnemonic);
editButton.setMnemonic(mnemonic);
}
#Override
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column)
{
if (value == null)
{
editButton.setText( "" );
editButton.setIcon( null );
}
else if (value instanceof Icon)
{
editButton.setText( "" );
editButton.setIcon( (Icon)value );
}
else
{
editButton.setText( value.toString() );
editButton.setIcon( null );
}
this.editorValue = value;
return editButton;
}
#Override
public Object getCellEditorValue()
{
return editorValue;
}
//
// Implement TableCellRenderer interface
//
#Override
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
if (isSelected)
{
renderButton.setForeground(table.getSelectionForeground());
renderButton.setBackground(table.getSelectionBackground());
}
else
{
renderButton.setForeground(table.getForeground());
renderButton.setBackground(UIManager.getColor("Button.background"));
}
if (hasFocus)
{
renderButton.setBorder( focusBorder );
}
else
{
renderButton.setBorder( originalBorder );
}
// renderButton.setText( (value == null) ? "" : value.toString() );
if (value == null)
{
renderButton.setText( "" );
renderButton.setIcon( null );
}
else if (value instanceof Icon)
{
renderButton.setText( "" );
renderButton.setIcon( (Icon)value );
}
else
{
renderButton.setText( value.toString() );
renderButton.setIcon( null );
}
return renderButton;
}
//
// Implement ActionListener interface
//
/*
* The button has been pressed. Stop editing and invoke the custom Action
*/
#Override
public void actionPerformed(ActionEvent e)
{
int row = table.convertRowIndexToModel( table.getEditingRow() );
fireEditingStopped();
// Invoke the Action
ActionEvent event = new ActionEvent(
table,
ActionEvent.ACTION_PERFORMED,
"" + row);
action.actionPerformed(event);
}
//
// Implement MouseListener interface
//
/*
* When the mouse is pressed the editor is invoked. If you then then drag
* the mouse to another cell before releasing it, the editor is still
* active. Make sure editing is stopped when the mouse is released.
*/
#Override
public void mousePressed(MouseEvent e)
{
if (table.isEditing()
&& table.getCellEditor() == this)
isButtonColumnEditor = true;
}
#Override
public void mouseReleased(MouseEvent e)
{
if (isButtonColumnEditor
&& table.isEditing())
table.getCellEditor().stopCellEditing();
isButtonColumnEditor = false;
}
#Override
public void mouseClicked(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
}
My code of actually doing so:
ButtonColumn deleteColumn = new ButtonColumn(eventTable, new AbstractAction(){
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("It works!!!!");
}
}, 3);
That never seems to be printed. I could really use some help. Thanks in advance!
EDIT: I've also tried this code and it also doesn't seem to work.
eventTable.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
JTable target = (JTable)e.getSource();
int row = target.getSelectedRow();
int column = target.getSelectedColumn();
System.out.println("Event table was clicked in row "+row+", column "+column);
}
});
Have a look at TableCellRenderer explained.
And the interface TableCellRenderer.
My guess is that the column is not editable which means the editor is never invoked so the Action is never invoked.
If this doesn't help then post a proper SSCCE demonstrating the problem.
I have a jtable with a custom editor and renderer applied to a column to turn the column's contents into a Button, however once i press on any of the buttons, the table loses focus, and the only interactive object in the panel is the button that was just clicked on, which can be clicked on repeatedly.
Here is the button renderer code:
public class ButtonRenderer implements TableCellRenderer {
private Border dsd_originalBorder;
private int in_mnemonic;
private Border dsd_focusBorder;
private JButton dsd_renderButton;
/**
* empty constructor
*/
public ButtonRenderer() {
dsd_renderButton = new JButton();
}
public Component getTableCellRendererComponent(JTable dsd_table, Object dsd_value,
boolean bo_isSelected, boolean bo_hasFocus, int in_row, int in_column) {
if (!WorkplaceConstants.STR_INACTIVE.equals(dsd_table.getValueAt(in_row, dsd_table
.getColumn(Main.hm_language.get(Language.STATUS))
.getModelIndex())))
return new JLabel("");
if (bo_isSelected) {
dsd_renderButton.setForeground(dsd_table.getSelectionForeground());
dsd_renderButton.setBackground(dsd_table.getSelectionBackground());
} else {
dsd_renderButton.setForeground(dsd_table.getForeground());
dsd_renderButton.setBackground(UIManager.getColor("Button.background"));
}
if (bo_hasFocus) {
dsd_renderButton.setBorder(dsd_focusBorder);
} else {
dsd_renderButton.setBorder(dsd_originalBorder);
}
// renderButton.setText( (value == null) ? "" : value.toString() );
if (dsd_value == null) {
dsd_renderButton.setText("");
dsd_renderButton.setIcon(null);
} else if (dsd_value instanceof Icon) {
dsd_renderButton.setText("");
dsd_renderButton.setIcon((Icon) dsd_value);
} else {
dsd_renderButton.setText(dsd_value.toString());
dsd_renderButton.setIcon(null);
}
return dsd_renderButton;
}
/**
* returns the mnemonic to activate the button when the cell has focus
*
* #return the mnemonic
*/
public int m_getMnemonic() {
return in_mnemonic;
}
/**
* The mnemonic to activate the button when the cell has focus
*
* #param p_in_mnemonic
* the mnemonic
*/
public void m_setMnemonic(int p_in_mnemonic) {
this.in_mnemonic = p_in_mnemonic;
dsd_renderButton.setMnemonic(p_in_mnemonic);
}
/**
* Get foreground color of the button when the cell has focus
*
* #return the foreground color
*/
public Border m_getFocusBorder() {
return dsd_focusBorder;
}
/**
* The foreground color of the button when the cell has focus
*
* #param dsd_focusBorder
* the foreground color
*/
public void m_setFocusBorder(Border dsd_focusBorder) {
this.dsd_focusBorder = dsd_focusBorder;
}
Here is the button Editor code:
public class ButtonEditor extends AbstractCellEditor implements TableCellEditor, ActionListener, MouseListener{
private JTable dsd_table;
private int in_mnemonic;
private Border dsd_originalBorder;
private Border dsd_focusBorder;
private JButton dsd_editButton;
private Object dsd_editorValue;
private boolean bo_isButtonColumnEditor;
/**
* Constructor
* #param dsdp_table the table to which the editor is going to be applied.
* #param action the action which is to be executed when the button is clicked
*/
public ButtonEditor(JTable dsdp_table) {
this.dsd_table = dsdp_table;
dsd_editButton = new JButton();
dsd_editButton.setFocusPainted( false );
dsd_editButton.addActionListener( this );
dsd_originalBorder = dsd_editButton.getBorder();
m_setFocusBorder( new LineBorder(Color.BLUE) );
dsdp_table.addMouseListener( this );
}
public Object getCellEditorValue() {
return dsd_editorValue;
}
/**
* returns the mnemonic to activate the button when the cell has focus
*
* #return the mnemonic
*/
public int m_getMnemonic()
{
return in_mnemonic;
}
/**
* The mnemonic to activate the button when the cell has focus
*
* #param in_mnemonic the mnemonic
*/
public void m_setMnemonic(int in_mnemonic)
{
this.in_mnemonic = in_mnemonic;
dsd_editButton.setMnemonic(in_mnemonic);
}
/**
* Get foreground color of the button when the cell has focus
*
* #return the foreground color
*/
public Border m_getFocusBorder() {
return dsd_focusBorder;
}
/**
* The foreground color of the button when the cell has focus
*
* #param dsdp_focusBorder
* the foreground color
*/
public void m_setFocusBorder(Border dsdp_focusBorder) {
this.dsd_focusBorder = dsdp_focusBorder;
dsd_editButton.setBorder(dsdp_focusBorder);
}
public void actionPerformed(ActionEvent arg0) {
int in_modelRow = dsd_table.convertRowIndexToModel( dsd_table.getEditingRow() );
fireEditingStopped();
// Here i start a custom thread to run in the background.
}
public void mouseClicked(MouseEvent arg0) {
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
if (dsd_table.isEditing() && dsd_table.getCellEditor() == this)
bo_isButtonColumnEditor = true;
}
public void mouseReleased(MouseEvent arg0) {
if (bo_isButtonColumnEditor && dsd_table.isEditing())
dsd_table.getCellEditor().stopCellEditing();
bo_isButtonColumnEditor = false;
}
public void addCellEditorListener(CellEditorListener arg0) {
}
public void cancelCellEditing() {
}
public void removeCellEditorListener(CellEditorListener arg0) {
}
public boolean shouldSelectCell(EventObject arg0) {
return false;
}
public boolean stopCellEditing() {
return false;
}
public Component getTableCellEditorComponent(JTable dsd_table, Object dsd_value,
boolean bo_isSelected, int in_row, int in_column) {
if (!WorkplaceConstants.STR_INACTIVE.equals(dsd_table.getValueAt(in_row, dsd_table
.getColumn(Main.hm_language.get(Language.STATUS))
.getModelIndex())))
return new JLabel("");
if (dsd_value == null)
{
dsd_editButton.setText( "" );
dsd_editButton.setIcon( null );
}
else if (dsd_value instanceof Icon)
{
dsd_editButton.setText( "" );
dsd_editButton.setIcon( (Icon)dsd_value );
}
else
{
dsd_editButton.setText( dsd_value.toString() );
dsd_editButton.setIcon( null );
}
bo_isButtonColumnEditor = true;
this.dsd_editorValue = dsd_value;
dsd_editButton.setBorder(dsd_originalBorder);
return dsd_editButton;
}
public boolean isCellEditable(EventObject e){
return true;
}
public Border m_getoriginalBorder() {
return dsd_originalBorder;
}
public void m_set_originalBorder(Border dsd_originalBorder) {
this.dsd_originalBorder = dsd_originalBorder;
}
}
Here is how i am assigning the editor and renderer to the table
public static void m_setButtonColumnConfiguration(JTable table) {
ButtonEditor dsd_btn_edit = new ButtonEditor(table);
dsd_btn_edit.m_setMnemonic(KeyEvent.VK_D);
table.getColumn(/*i get the identifier for the column here*/).setCellEditor(dsd_btn_edit);
ButtonRenderer dsd_btn_rend = new ButtonRenderer();
dsd_btn_rend.m_setMnemonic(KeyEvent.VK_D);
table.getColumn(/*i get the identifier for the column here*/)
.setCellRenderer(dsd_btn_rend);
}
Button, however once i press on any of the buttons, the table loses focus
Yes, anytime you click on a component it gets focus, so this makes sense.
and the only interactive object in the panel is the button that was just clicked on,
Not sure I understand this statement. You can click on the table again and it will regain focus. You can then navigate around the table.
Maybe you are just suggesting the button renderer/editor isn't working the way you expect. We can't test this because you didn't post a SSCCE.
Anyway, check out Table Button Column for another example of a button renderer/editor that responds to mouse clicks or key board events.
I want to show a JButton within a JTable. This is nothing special, and I found a lot of examples doing this. However, I always have issues with pressing the buttons via Keyboard (not via mouse). I expect that I can select a cell and push (also visually) the button by pressing SPACE (no mnemonics).
Two snippets work like a charm, except supporting keys:
http://tips4java.wordpress.com/2009/07/12/table-button-column/
The author claims that keys work. I believe that they did, but not on all my systems I checked. However, supported mnemonics work perfectly.
(posted here: Adding Jbutton to JTable)
http://www.java2s.com/Code/Java/Swing-Components/ButtonTableExample.htm
(posted here: Adding Jbutton to JTable)
In the example, it works perfectly! However, it doesn't work for my table. Just disable row selection (I have to use cell selection), and pressing the button via key doesn't work anymore:
table.setRowSelectionAllowed(false);
I tried hard figuring out what's going wrong or how to fix it, but I failed. My only achievement is to call the action behind the button, but the button is not pressed (I mean the visual behavior).
Some information added:
I used... (in many combinations)
Ubuntu 10.04, Windows 7, Windows 8
Java 7u21, JDK 1.6.0_33, OpenJDK Runtime Environment (IcedTea6 1.8.1) (6b18-1.8.1-0ubuntu1)
WindowsLookAndFeel, Metal (Cross Platform LAF), Nimbus
0% success!
TableTest.java
import java.awt.event.ActionEvent;
import java.util.LinkedList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;
public class TableTest extends JFrame {
public TableTest() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTable table = new JTable(new TestModel());
table.setRowSelectionAllowed(false);
table.getColumnModel().getColumn(1).setPreferredWidth(3);
table.getColumnModel().getColumn(2).setPreferredWidth(3);
this.add(new JScrollPane(table));
Action increase = new AbstractAction("+") {
#Override
public void actionPerformed(ActionEvent e) {
JTable table = (JTable) e.getSource();
int row = Integer.valueOf(e.getActionCommand());
TestModel model = (TestModel) table.getModel();
model.increment(row, 0);
}
};
ButtonColumn inc = new ButtonColumn(table, increase, 1);
Action decrease = new AbstractAction("-") {
#Override
public void actionPerformed(ActionEvent e) {
JTable table = (JTable) e.getSource();
int row = Integer.valueOf(e.getActionCommand());
TestModel model = (TestModel) table.getModel();
model.decrement(row, 0);
}
};
ButtonColumn dec = new ButtonColumn(table, decrease, 2);
pack();
}
public static void main(String[] args) {
new TableTest().setVisible(true);
}
}
class TestModel extends AbstractTableModel {
List<TestRecord> records = new LinkedList<TestRecord>();
private static class TestRecord {
private int val = 0;
}
public void increment(int row, int col) {
records.get(row).val++;
fireTableCellUpdated(row, 0);
}
public void decrement(int row, int col) {
records.get(row).val--;
fireTableCellUpdated(row, 0);
}
public TestModel() {
records.add(new TestRecord());
records.add(new TestRecord());
}
#Override
public Class<?> getColumnClass(int col) {
if (col == 0) {
return Integer.class;
} else {
return ButtonColumn.class;
}
}
#Override
public boolean isCellEditable(int row, int col) {
return true;
}
#Override
public int getColumnCount() {
return 3;
}
#Override
public int getRowCount() {
return records.size();
}
#Override
public Object getValueAt(int row, int col) {
if (col == 0) {
return records.get(row).val;
} else if (col == 1) {
return "+";
} else {
return "-";
}
}
}
ButtonColumn.java
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.AbstractCellEditor;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
/**
* The ButtonColumn class provides a renderer and an editor that looks like a
* JButton. The renderer and editor will then be used for a specified column in
* the table. The TableModel will contain the String to be displayed on the
* button.
*
* The button can be invoked by a mouse click or by pressing the space bar when
* the cell has focus. Optionally a mnemonic can be set to invoke the button.
* When the button is invoked the provided Action is invoked. The source of the
* Action will be the table. The action command will contain the model row
* number of the button that was clicked.
*
*/
public class ButtonColumn extends AbstractCellEditor implements
TableCellRenderer, TableCellEditor, ActionListener, MouseListener {
private JTable table;
private Action action;
private int mnemonic;
private Border originalBorder;
private Border focusBorder;
private JButton renderButton;
private JButton editButton;
private Object editorValue;
private boolean isButtonColumnEditor;
/**
* Create the ButtonColumn to be used as a renderer and editor. The renderer
* and editor will automatically be installed on the TableColumn of the
* specified column.
*
* #param table
* the table containing the button renderer/editor
* #param action
* the Action to be invoked when the button is invoked
* #param column
* the column to which the button renderer/editor is added
*/
public ButtonColumn(JTable table, Action action, int column) {
this.table = table;
this.action = action;
renderButton = new JButton();
editButton = new JButton();
editButton.setFocusPainted(false);
editButton.addActionListener(this);
originalBorder = editButton.getBorder();
setFocusBorder(new LineBorder(Color.BLUE));
TableColumnModel columnModel = table.getColumnModel();
columnModel.getColumn(column).setCellRenderer(this);
columnModel.getColumn(column).setCellEditor(this);
table.addMouseListener(this);
}
/**
* Get foreground color of the button when the cell has focus
*
* #return the foreground color
*/
public Border getFocusBorder() {
return focusBorder;
}
/**
* The foreground color of the button when the cell has focus
*
* #param focusBorder
* the foreground color
*/
public void setFocusBorder(Border focusBorder) {
this.focusBorder = focusBorder;
editButton.setBorder(focusBorder);
}
public int getMnemonic() {
return mnemonic;
}
/**
* The mnemonic to activate the button when the cell has focus
*
* #param mnemonic
* the mnemonic
*/
public void setMnemonic(int mnemonic) {
this.mnemonic = mnemonic;
renderButton.setMnemonic(mnemonic);
editButton.setMnemonic(mnemonic);
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
if (value == null) {
editButton.setText("");
editButton.setIcon(null);
} else if (value instanceof Icon) {
editButton.setText("");
editButton.setIcon((Icon) value);
} else {
editButton.setText(value.toString());
editButton.setIcon(null);
}
this.editorValue = value;
return editButton;
}
#Override
public Object getCellEditorValue() {
return editorValue;
}
//
// Implement TableCellRenderer interface
//
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
if (isSelected) {
renderButton.setForeground(table.getSelectionForeground());
renderButton.setBackground(table.getSelectionBackground());
} else {
renderButton.setForeground(table.getForeground());
renderButton.setBackground(UIManager.getColor("Button.background"));
}
if (hasFocus) {
renderButton.setBorder(focusBorder);
} else {
renderButton.setBorder(originalBorder);
}
// renderButton.setText( (value == null) ? "" : value.toString() );
if (value == null) {
renderButton.setText("");
renderButton.setIcon(null);
} else if (value instanceof Icon) {
renderButton.setText("");
renderButton.setIcon((Icon) value);
} else {
renderButton.setText(value.toString());
renderButton.setIcon(null);
}
return renderButton;
}
//
// Implement ActionListener interface
//
/*
* The button has been pressed. Stop editing and invoke the custom Action
*/
#Override
public void actionPerformed(ActionEvent e) {
int row = table.convertRowIndexToModel(table.getEditingRow());
fireEditingStopped();
// Invoke the Action
ActionEvent event = new ActionEvent(table,
ActionEvent.ACTION_PERFORMED, "" + row);
action.actionPerformed(event);
}
//
// Implement MouseListener interface
//
/*
* When the mouse is pressed the editor is invoked. If you then then drag
* the mouse to another cell before releasing it, the editor is still
* active. Make sure editing is stopped when the mouse is released.
*/
#Override
public void mousePressed(MouseEvent e) {
if (table.isEditing() && table.getCellEditor() == this)
isButtonColumnEditor = true;
}
#Override
public void mouseReleased(MouseEvent e) {
if (isButtonColumnEditor && table.isEditing())
table.getCellEditor().stopCellEditing();
isButtonColumnEditor = false;
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
The problem isn't the editor. The SPACE key stroke is not forwarded to the default editor in the first column either.
The problem is that JTable defines an Action for the SPACE key so it is intercepted before it has a chance to be passed to the editor. Search my blog for the Key Bindings entry where you will find a program that lists all the default Key Bindings for JTable. The Action that is invoked is called "addToSelection", so I'm not sure why it works differently depending on the row selection.
Anyway one solution is to remove this Action:
InputMap im = table.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
KeyStroke space = KeyStroke.getKeyStroke("SPACE");
im.put(space, "none");
Both TableTest, which uses ButtonColumn, and TablePopupEditor are complete examples that work correctly when the Space key is pressed in a selected button cell. Neither evinces the typical ButtonModel-defined appearance of a stand-alone button, but you can provide your own visual queues as required. This related example uses a colored border.
I am making a hospital project in Java, I have made a JTable which is fetching Hospital Name and a Hospital image link i.e "Click to see more" from SQL database. My problem is that the data is successfully fetched from database to the table, but I can't click the link which is in the table cell.
How to make the link active?
Consider using JXTable (a class of SwingX): it supports a hyperlink renderer which can be configured to do any action, based on the cell value
JXTable table = new JXTable(myModel);
AbstractHyperlinkAction<Object> simpleAction = new AbstractHyperlinkAction<Object>(null) {
public void actionPerformed(ActionEvent e) {
// here goes what you want to do on activating the hyperlink
//LOG.info("hit: " + getTarget());
}
};
TableCellRenderer renderer = new DefaultTableRenderer(
new HyperlinkProvider(simpleAction));
table.getColumnExt(0).setEditable(false);
table.getColumnExt(0).setCellRenderer(renderer);
You can either make a TableCellEditor whose isCellEditable method could be used to activate on a single mouse click. Frankly, this just get messy.
Or, you could attach a MouseListener to the table directly and monitor for the mouseClicked event.
On the clicked event, you could need to getSelectedColumn and getSelectedRow to determine if they've clicked on the column you want and get the link value from the selected cell, using getValueAt
You'll need to take into consideration that the table may be sorted or the columns are no longer in the order you started them in (the user may have moved them).
Then you'll need convertColumnIndexToView and convertRowIndexToModel
Simple :D
I think that avoiding an external library is a better way to go as MadProgrammer suggests, but I guess swingx is still pure java. Anyway, I would use the mouseClicked listener and then open uri in the way described in this question How to add hyperlink in JLabel.
JTable table = new JTable();
table.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
int row = table.getSelectedRow();
int col = table.getSelectedColumn();
//build your address / link
URI uri = new URI("http: your link here");
//see below
open(uri);
}
});
//Then elsewhere as from the McDowell answer
private static void open(URI uri) {
if (Desktop.isDesktopSupported()) {
try {
Desktop.getDesktop().browse(uri);
} catch (IOException e) { /* TODO: error handling */ }
} else { /* TODO: error handling */ }
}
I solved adding a button inside the cells clickable, I followed this tutorial Table Button Column from Rob Camick:
String[] columnNames = {"First Name", "Last Name", ""};
Object[][] data =
{
{"Homer", "Simpson", "delete Homer"},
{"Madge", "Simpson", "delete Madge"},
{"Bart", "Simpson", "delete Bart"},
{"Lisa", "Simpson", "delete Lisa"},
};
DefaultTableModel model = new DefaultTableModel(data, columnNames);
JTable table = new JTable( model );
Then:
Action delete = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
JTable table = (JTable)e.getSource();
int modelRow = Integer.valueOf( e.getActionCommand() );
((DefaultTableModel)table.getModel()).removeRow(modelRow);
}
};
ButtonColumn buttonColumn = new ButtonColumn(table, delete, 2);
buttonColumn.setMnemonic(KeyEvent.VK_D);
Where ButtonColumn class is:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.*;
/**
* The ButtonColumn class provides a renderer and an editor that looks like a
* JButton. The renderer and editor will then be used for a specified column
* in the table. The TableModel will contain the String to be displayed on
* the button.
*
* The button can be invoked by a mouse click or by pressing the space bar
* when the cell has focus. Optionally a mnemonic can be set to invoke the
* button. When the button is invoked the provided Action is invoked. The
* source of the Action will be the table. The action command will contain
* the model row number of the button that was clicked.
*
*/
public class ButtonColumn extends AbstractCellEditor
implements TableCellRenderer, TableCellEditor, ActionListener, MouseListener
{
private JTable table;
private Action action;
private int mnemonic;
private Border originalBorder;
private Border focusBorder;
private JButton renderButton;
private JButton editButton;
private Object editorValue;
private boolean isButtonColumnEditor;
/**
* Create the ButtonColumn to be used as a renderer and editor. The
* renderer and editor will automatically be installed on the TableColumn
* of the specified column.
*
* #param table the table containing the button renderer/editor
* #param action the Action to be invoked when the button is invoked
* #param column the column to which the button renderer/editor is added
*/
public ButtonColumn(JTable table, Action action, int column)
{
this.table = table;
this.action = action;
renderButton = new JButton();
editButton = new JButton();
editButton.setFocusPainted( false );
editButton.addActionListener( this );
originalBorder = editButton.getBorder();
setFocusBorder( new LineBorder(Color.BLUE) );
TableColumnModel columnModel = table.getColumnModel();
columnModel.getColumn(column).setCellRenderer( this );
columnModel.getColumn(column).setCellEditor( this );
table.addMouseListener( this );
}
/**
* Get foreground color of the button when the cell has focus
*
* #return the foreground color
*/
public Border getFocusBorder()
{
return focusBorder;
}
/**
* The foreground color of the button when the cell has focus
*
* #param focusBorder the foreground color
*/
public void setFocusBorder(Border focusBorder)
{
this.focusBorder = focusBorder;
editButton.setBorder( focusBorder );
}
public int getMnemonic()
{
return mnemonic;
}
/**
* The mnemonic to activate the button when the cell has focus
*
* #param mnemonic the mnemonic
*/
public void setMnemonic(int mnemonic)
{
this.mnemonic = mnemonic;
renderButton.setMnemonic(mnemonic);
editButton.setMnemonic(mnemonic);
}
#Override
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column)
{
if (value == null)
{
editButton.setText( "" );
editButton.setIcon( null );
}
else if (value instanceof Icon)
{
editButton.setText( "" );
editButton.setIcon( (Icon)value );
}
else
{
editButton.setText( value.toString() );
editButton.setIcon( null );
}
this.editorValue = value;
return editButton;
}
#Override
public Object getCellEditorValue()
{
return editorValue;
}
//
// Implement TableCellRenderer interface
//
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
if (isSelected)
{
renderButton.setForeground(table.getSelectionForeground());
renderButton.setBackground(table.getSelectionBackground());
}
else
{
renderButton.setForeground(table.getForeground());
renderButton.setBackground(UIManager.getColor("Button.background"));
}
if (hasFocus)
{
renderButton.setBorder( focusBorder );
}
else
{
renderButton.setBorder( originalBorder );
}
// renderButton.setText( (value == null) ? "" : value.toString() );
if (value == null)
{
renderButton.setText( "" );
renderButton.setIcon( null );
}
else if (value instanceof Icon)
{
renderButton.setText( "" );
renderButton.setIcon( (Icon)value );
}
else
{
renderButton.setText( value.toString() );
renderButton.setIcon( null );
}
return renderButton;
}
//
// Implement ActionListener interface
//
/*
* The button has been pressed. Stop editing and invoke the custom Action
*/
public void actionPerformed(ActionEvent e)
{
int row = table.convertRowIndexToModel( table.getEditingRow() );
fireEditingStopped();
// Invoke the Action
ActionEvent event = new ActionEvent(
table,
ActionEvent.ACTION_PERFORMED,
"" + row);
action.actionPerformed(event);
}
//
// Implement MouseListener interface
//
/*
* When the mouse is pressed the editor is invoked. If you then then drag
* the mouse to another cell before releasing it, the editor is still
* active. Make sure editing is stopped when the mouse is released.
*/
public void mousePressed(MouseEvent e)
{
if (table.isEditing()
&& table.getCellEditor() == this)
isButtonColumnEditor = true;
}
public void mouseReleased(MouseEvent e)
{
if (isButtonColumnEditor
&& table.isEditing())
table.getCellEditor().stopCellEditing();
isButtonColumnEditor = false;
}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
The default behavior of a JTable is to append to the contents when you start typing, and to place the caret at the clicked location when clicking. I want the behavior of both these things to change, so the contents is replaced when I edit a cell, either by typing or by clicking and then typing. When I click a cell and then change the caret position, however, I want the contents to stay so I can change it.
I know how to select all when the cell becomes editing, by replacing the cell editor with one that selects all inside a SwingUtilities.invokeLater (see elsewhere), but that causes the typing behavior to break. When I do this and start typing in a cell, first the typed character is appended to the string, then it is selected (but the selection is invisible!) and when typing another character the contents gets replaced by that.
Is there a way to replace the contents immediately when typing in a highlighted (but not editing) cell, but select all when clicking a cell?
Here is the code I use for the CellEditor:
public class TextFieldCellEditor extends JTextField implements TableCellEditor
{
private CellEditorListener cellEditorListener = null;
private boolean isInteger = false;
private Object oldValue;
// Start editing
#Override
public Component getTableCellEditorComponent(JTable table, Object obj, boolean isSelected, int row, int column)
{
Color color2 = DefaultLookup.getColor(this, ui, "Table.alternateRowColor");
super.setBackground(color2 != null && (row & 1) == 1? color2 : table.getBackground());
super.setForeground(table.getForeground());
super.setBorder(DefaultLookup.getBorder(this, ui, "Table.focusCellHighlightBorder"));
super.setText(obj.toString());
isInteger = obj instanceof Integer;
if (isInteger)
{
super.setHorizontalAlignment(SwingConstants.RIGHT);
oldValue = obj;
}
// SwingUtilities.invokeLater(new Runnable()
// {
// public void run()
// {
// TextFieldCellEditor.this.selectAll();
// }
// });
return this;
}
// Retrieve e dited value
#Override
public Object getCellEditorValue()
{
if (isInteger)
{
// Try to convert to integer. If input is invalid, revert.
try
{
return new Integer(super.getText());
}
catch (NumberFormatException e)
{
return oldValue;
}
}
return super.getText();
}
#Override
public boolean isCellEditable(EventObject e)
{
return true;
}
#Override
public boolean shouldSelectCell(EventObject e)
{
return true;
}
#Override
public boolean stopCellEditing()
{
cellEditorListener.editingStopped(new ChangeEvent(this));
return true;
}
#Override
public void cancelCellEditing()
{
cellEditorListener.editingCanceled(new ChangeEvent(this));
}
#Override
public void addCellEditorListener(CellEditorListener celleditorlistener)
{
cellEditorListener = celleditorlistener;
}
#Override
public void removeCellEditorListener(CellEditorListener celleditorlistener)
{
if (cellEditorListener == cellEditorListener) cellEditorListener = null;
}
}
In your getTableCellEditorComponent() implementation, add the following:
if (isSelected) {
this.selectAll();
}
As an aside, why not extend AbstractCellEditor or DefaultCellEditor(JTextField textField)? See also How to Use Tables: Using Other Editors.
Addendum: See also Table Select All Renderer and Table Select All Editor.
The cleanest solution I could find for this case was to overwrite the JTable's editCellAt and inform the CellEditor of how the edit was triggered:
#Override
public boolean editCellAt(int row, int column, EventObject e) {
cellEditor.setKeyTriggered(e instanceof KeyEvent);
return super.editCellAt(row, column, e);
}
And here is the relevant CellEditor code:
public class MyCellEditor extends DefaultCellEditor {
private boolean keyTriggered;
public MyCellEditor() {
super(new JTextField());
final JTextField textField = (JTextField) getComponent();
textField.addFocusListener(new FocusAdapter() {
#Override
public void focusGained(FocusEvent e) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if (!keyTriggered) {
textField.selectAll();
}
}
});
}
});
}
public void setKeyTriggered(boolean keyTriggered) {
this.keyTriggered = keyTriggered;
}
#Override
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column) {
final JTextField textField = (JTextField)
super.getTableCellEditorComponent(table, value, isSelected, row, column);
textField.selectAll();
return textField;
}
}