How to Add Button in a Row of Jtable in Swing Java

Adding Jbutton to JTable

###Add button to JTable

JTable table = new JTable(new JTableModel()); 
JScrollPane scrollPane = new JScrollPane(table);
table.setFillsViewportHeight(true);

TableCellRenderer buttonRenderer = new JTableButtonRenderer();
table.getColumn("Button1").setCellRenderer(buttonRenderer);
table.getColumn("Button2").setCellRenderer(buttonRenderer);

###Sample JTableModel, This is manage the columns and rows, Setting components

public static class JTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
private static final String[] COLUMN_NAMES = new String[] {"Id", "Stuff", "Button1", "Button2"};
private static final Class<?>[] COLUMN_TYPES = new Class<?>[] {Integer.class, String.class, JButton.class, JButton.class};

@Override public int getColumnCount() {
return COLUMN_NAMES.length;
}

@Override public int getRowCount() {
return 4;
}

@Override public String getColumnName(int columnIndex) {
return COLUMN_NAMES[columnIndex];
}

@Override public Class<?> getColumnClass(int columnIndex) {
return COLUMN_TYPES[columnIndex];
}

@Override public Object getValueAt(final int rowIndex, final int columnIndex) {
/*Adding components*/
switch (columnIndex) {
case 0: return rowIndex;
case 1: return "Text for "+rowIndex;
case 2: // fall through
/*Adding button and creating click listener*/
case 3: final JButton button = new JButton(COLUMN_NAMES[columnIndex]);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(button),
"Button clicked for row "+rowIndex);
}
});
return button;
default: return "Error";
}
}
}

###Sample Button click listener , This manage the when mouse is clicked over the component

private static class JTableButtonMouseListener extends MouseAdapter {
private final JTable table;

public JTableButtonMouseListener(JTable table) {
this.table = table;
}

public void mouseClicked(MouseEvent e) {
int column = table.getColumnModel().getColumnIndexAtX(e.getX()); // get the coloum of the button
int row = e.getY()/table.getRowHeight(); //get the row of the button

/*Checking the row or column is valid or not*/
if (row < table.getRowCount() && row >= 0 && column < table.getColumnCount() && column >= 0) {
Object value = table.getValueAt(row, column);
if (value instanceof JButton) {
/*perform a click event*/
((JButton)value).doClick();
}
}
}
}

###Sample JTable Cell Renderer, Managing the cell component

private static class JTableButtonRenderer implements TableCellRenderer {        
@Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
JButton button = (JButton)value;
return button;
}
}

How to add button to row of JTable?

To display a button in a column you need to create:

  1. a custom renderer to display the JButton
  2. a custom editor to respond to the mouse click

Read the section from the Swing tutorial on How to Use Tables. The section on:

  1. Using Custom Renders will explain the basics of using a renderer
  2. Using Other Editors will explain the basics of using an editor

Working example are provided in the tutorial that you can download.

You can check out Table Button Column for one approach.

The code uses a single class to implement the custom renderer and editor that you will need for the column to display your text as a button.

How to add buttons inside a cell of jtable and give it action

You need both a renderer and and editor, as shown in this example. See How to Use Tables: Editors and Renderers for details. Tangentially, you should override the method isCellEditable() in your TableModel rather than extending JTable.

Add JButtons only to certain rows and columns in a JTable

here is a mcve reduced to the very basics:

public class ButtonTableTest {

public static void main(String[] args) {
final Random random = new Random();
DefaultTableModel tableModel = new DefaultTableModel(20, 7) {
@Override
public Class<?> getColumnClass(int arg0) {
// provide the default renderer and editor of String for empty cells
return String.class;
}

@Override
public boolean isCellEditable(int row, int column) {
// do not request the editor for empty cells
return !"".equals(getValueAt(row, column));
}

@Override
public Object getValueAt(int row, int column) {
// some random table content
if (null == super.getValueAt(row, column)) {
int nextInt = random.nextInt(10);
if (nextInt > 5)
super.setValueAt(String.format("cell %dx%d", row, column), row, column);
else
super.setValueAt("", row, column);
}
return super.getValueAt(row, column);
}

@Override
public void setValueAt(Object arg0, int arg1, int arg2) {
// prevent update to NULL
}

};

JTable jTable = new JTable(tableModel);
jTable.setPreferredSize(new Dimension(800, 350));
final JButton jButton = new JButton();

jTable.setDefaultRenderer(String.class, new DefaultTableCellRenderer() {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
Component tableCellRendererComponent = super.getTableCellRendererComponent(table, value, isSelected,
hasFocus, row, column);
if ("".equals(value)) {
// default renderer for empty cells
return tableCellRendererComponent;
} else {
jButton.setAction(createSameActionForEditorAndRenderer(table, value));
return jButton;
}
}
});
jTable.setDefaultEditor(String.class, new DefaultCellEditor(new JCheckBox()) { // JCheckBox is closest to a button...

@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
int column) {
Component tableCellEditorComponent = super.getTableCellEditorComponent(table, value, isSelected, row,
column);
jButton.setAction(createSameActionForEditorAndRenderer(jTable, value));
return jButton;
}

});
JOptionPane.showMessageDialog(null, jTable);
}

private static AbstractAction createSameActionForEditorAndRenderer(JTable table, Object value) {
return new AbstractAction((String) value) {
@Override
public void actionPerformed(ActionEvent arg0) {
SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(table, String.format("clicked on %s",value));
});
table.getCellEditor().stopCellEditing();
table.repaint();
}
};
}
}

When I click into a cell, it often shows the wrong value. Sometimes its the value of another cell from the same row or from a cell that has been previously clicked. I can't figure out why it does that?

This is because I use the same JButton instance for the Renderer as well as the Editor.

This is the change to fix it:

        //final JButton jButton = new JButton();

jTable.setDefaultRenderer(String.class, new DefaultTableCellRenderer() {
private final JButton jButton = new JButton();
// rest of renderer

jTable.setDefaultEditor(String.class, new DefaultCellEditor(new JCheckBox()) { // JCheckBox is closest to a button...
private final JButton jButton = new JButton();
// rest of editor

How to create a jtable with different buttons in each row

I would guess that EnumSet is what you are looking for:

Sample Image

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.table.*;

public final class EnumSetTest {
private JComponent makeUI() {
String[] columnNames = {"REPORT ID", "ACTION"};
Object[][] data = {
{"Report1", EnumSet.of(Actions.PRINT)},
{"Report2", EnumSet.of(Actions.PRINT, Actions.EDIT)},
{"Report3", EnumSet.allOf(Actions.class)},
{"Report4", EnumSet.of(Actions.PRINT)}
};
DefaultTableModel model = new DefaultTableModel(data, columnNames) {
@Override public Class<?> getColumnClass(int column) {
return getValueAt(0, column).getClass();
}
};
JTable table = new JTable(model);
table.setRowHeight(36);
TableColumn column = table.getColumnModel().getColumn(1);
column.setCellRenderer(new ButtonsRenderer());
column.setCellEditor(new ButtonsEditor(table));
return new JScrollPane(table);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new EnumSetTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}

enum Actions { PRINT, EDIT; }

class ButtonsPanel extends JPanel {
public final List<JButton> buttons = new ArrayList<>();
public ButtonsPanel() {
super(new FlowLayout(FlowLayout.LEFT));
setOpaque(true);
for (Actions a : Actions.values()) {
JButton b = new JButton(a.toString());
b.setFocusable(false);
b.setRolloverEnabled(false);
add(b);
buttons.add(b);
}
}
protected void updateButtons(Object value) {
if (value instanceof EnumSet) {
EnumSet ea = (EnumSet) value;
removeAll();
if (ea.contains(Actions.PRINT)) {
add(buttons.get(0));
}
if (ea.contains(Actions.EDIT)) {
add(buttons.get(1));
}
}
}
}

class ButtonsRenderer implements TableCellRenderer {
private final ButtonsPanel panel = new ButtonsPanel();
@Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
panel.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
panel.updateButtons(value);
return panel;
}
}

class PrintAction extends AbstractAction {
private final JTable table;
public PrintAction(JTable table) {
super(Actions.PRINT.toString());
this.table = table;
}
@Override public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(table, "Printing");
}
}

class EditAction extends AbstractAction {
private final JTable table;
public EditAction(JTable table) {
super(Actions.EDIT.toString());
this.table = table;
}
@Override public void actionPerformed(ActionEvent e) {
int row = table.convertRowIndexToModel(table.getEditingRow());
Object o = table.getModel().getValueAt(row, 0);
JOptionPane.showMessageDialog(table, "Editing: " + o);
}
}

class ButtonsEditor extends AbstractCellEditor implements TableCellEditor {
private final ButtonsPanel panel = new ButtonsPanel();
private final JTable table;
private Object o;
private class EditingStopHandler extends MouseAdapter implements ActionListener {
@Override public void mousePressed(MouseEvent e) {
Object o = e.getSource();
if (o instanceof TableCellEditor) {
actionPerformed(null);
} else if (o instanceof JButton) {
ButtonModel m = ((JButton) e.getComponent()).getModel();
if (m.isPressed() && table.isRowSelected(table.getEditingRow()) && e.isControlDown()) {
panel.setBackground(table.getBackground());
}
}
}
@Override public void actionPerformed(ActionEvent e) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
fireEditingStopped();
}
});
}
}
public ButtonsEditor(JTable table) {
super();
this.table = table;
panel.buttons.get(0).setAction(new PrintAction(table));
panel.buttons.get(1).setAction(new EditAction(table));

EditingStopHandler handler = new EditingStopHandler();
for (JButton b : panel.buttons) {
b.addMouseListener(handler);
b.addActionListener(handler);
}
panel.addMouseListener(handler);
}
@Override public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column) {
panel.setBackground(table.getSelectionBackground());
panel.updateButtons(value);
o = value;
return panel;
}
@Override public Object getCellEditorValue() {
return o;
}
}

JButtons in JTable cell change shape on row selection

According to the source code, the border inset of the table cell editor is 0 according to the FlatBorder specification.

com/formdev/flatlaf/ui/FlatBorder.java

protected boolean isCellEditor( Component c ) {
return FlatUIUtils.isCellEditor( c );
}

@Override
public Insets getBorderInsets( Component c, Insets insets ) {
// ...

if( isCellEditor( c ) ) {
// remove top and bottom insets if used as cell editor
insets.top = insets.bottom = 0;
// ...

com/formdev/flatlaf/ui/FlatUIUtils.java

public static boolean isCellEditor( Component c ) {
// check whether used in cell editor (check 3 levels up)

Any ideas about how to rectify this?

Make the cell editor panel hierarchy three or more levels.

class TableRowPanel1 extends JPanel {
protected final JButton jButton1 = new JButton("jButton1");
protected final JButton jButton2 = new JButton("jButton2");

public TableRowPanel1() {
super(new BorderLayout());
JPanel p2 = new JPanel(new FlowLayout(FlowLayout.LEADING));
p2.add(jButton1);
p2.add(jButton2);
JPanel p1 = new JPanel(new BorderLayout());
p1.add(p2);
add(p1);
}
}

or override FlatBorder#isCellEditor(Component c) method to always return false.

class TableRowPanel1 extends JPanel {
protected final JButton jButton1 = new JButton("jButton1");
protected final JButton jButton2 = new JButton("jButton2");

public TableRowPanel1() {
super(new FlowLayout(FlowLayout.LEADING));
FlatBorder border = new FlatButtonBorder() {
@Override protected boolean isCellEditor(Component c) {
return false;
}
};
jButton1.setBorder(border);
jButton2.setBorder(border);
add(jButton1);
add(jButton2);
}
}

NewJFrame2.java

import com.formdev.flatlaf.*;
import com.formdev.flatlaf.ui.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.table.*;

public class NewJFrame2 {
public Component makeUI() {
List<Data> modelData = new ArrayList<>();
for (int i = 0; i < 4; i++) {
Data data = new Data();
data.value = "Data_" + i;
modelData.add(data);
}
JTable table = new JTable(new MyTableModel(modelData));
table.setDefaultRenderer(Data.class, new MyCellRenderer());
table.setDefaultEditor(Data.class, new MyCellEditor());
table.setRowHeight(52);

JPanel p = new JPanel(new BorderLayout());
p.add(new JScrollPane(table));
p.add(new TableRowPanel1(), BorderLayout.SOUTH);
return p;
}

public static void main(String[] args) {
EventQueue.invokeLater(() -> {
FlatDarkLaf.setup();
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new NewJFrame2().makeUI());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}

class Data {
String value;
}

class MyTableModel extends AbstractTableModel {
private List<Data> data;

public MyTableModel(List<Data> data) {
this.data = data;
}

@Override
public int getRowCount() {
return data.size();
}

@Override
public int getColumnCount() {
return 1;
}

@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return data.get(rowIndex);
}

@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
}

@Override
public Class<?> getColumnClass(int columnIndex) {
return Data.class;
}
}

class MyCellRenderer implements TableCellRenderer {
protected final TableRowPanel1 renderer = new TableRowPanel1();

@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
return renderer;
}
}

class MyCellEditor extends AbstractCellEditor implements TableCellEditor {
protected final TableRowPanel1 editor = new TableRowPanel1();

@Override
public Object getCellEditorValue() {
return null;
}

@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
return editor;
}
}

class TableRowPanel1 extends JPanel {
protected final JButton jButton1 = new JButton("jButton1");
protected final JButton jButton2 = new JButton("jButton2");

public TableRowPanel1() {
super(new BorderLayout());
JPanel p2 = new JPanel(new FlowLayout(FlowLayout.LEADING));
p2.add(jButton1);
p2.add(jButton2);
JPanel p1 = new JPanel(new BorderLayout());
p1.add(p2);
add(p1);
}
}

// class TableRowPanel1 extends JPanel {
// protected final JButton jButton1 = new JButton("jButton1");
// protected final JButton jButton2 = new JButton("jButton2");
//
// public TableRowPanel1() {
// super(new FlowLayout(FlowLayout.LEADING));
// FlatBorder border = new FlatButtonBorder() {
// @Override protected boolean isCellEditor(Component c) {
// return false;
// }
// };
// jButton1.setBorder(border);
// jButton2.setBorder(border);
// add(jButton1);
// add(jButton2);
// }
// }

java pass data to JTable's row upon button click

What you need to do is add a row to the Model. Something like this

public void actionPerformed(ActionEvent arg0) {
DefaultTableModel model = (DefaultTableModel)table.getModel();
model.addRow(new Object[]{"DN Korina", "DN Madrid", "DN Romania"});
}

The easiest way is to set up you model before hand to have no rows

String[] colNames = {
"QTY", "Item Code", "Amount"
};

DefaultTableModel model = new DefaultTableModel(colNames, 0); <== 0 rows
JTable table = new JTable(model);

Then whatever rows you want to add, just add to the model

public void actionPerformed(ActionEvent e){
model.addRow(new Object[]{"DN Korina", "DN Madrid", "DN Romania"});
}

UPDATE Example

import java.awt.*;
import java.util.Stack;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;

public class TestTable {

private String[] colNames = { "Col 1", "Col2", "Col3", "Col4", "Col5" };

private DefaultTableModel model = new DefaultTableModel(colNames, 0);
private JTable table = new JTable(model);
private MyStack myStack = new MyStack();
private Stack<Integer[]> stack;

private JButton button = new JButton("Add Row");

public TestTable() {
stack = myStack.getStack();

button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!stack.isEmpty()) {
Integer[] array = stack.pop();
model.addRow(array);
}
}
});

JFrame frame = new JFrame();
frame.add(new JScrollPane(table), BorderLayout.CENTER);
frame.add(button, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestTable();
}
});
}
}

class MyStack {
public Stack<Integer[]> stack = new Stack<Integer[]>();

public MyStack() {
int k = 1;
for (int i = 0; i < 20; i++) {
Integer[] array = new Integer[5];
for (int j = 0; j < 5; j++) {
array[j] = i * k * (j + 1);
}
k++;
stack.push(array);
}
}

public Stack<Integer[]> getStack() {
return stack;
}
}

Explanation

  • I have a MyStack class that has a Stack. It could be any data structure like an array even, but I chose to use a Stack because I can just .pop() it. And the get the Integer[].
  • I instantiate that class in the GUI class
  • I then extract that Stack. Keep in mind the Stack holds Integer[]'s
  • As i explained, you want to add a single dimension array to the model.
  • So every time I click the button, an Integer[] is pulled out of the Stack, then I just add that array to the model as a row.


Related Topics



Leave a reply



Submit