I searched for the answer for quite awhile , but most cases were different from mine.
I want to add a JButton directly after the last row of the table, the table is dynamic and is inside a scrollPane.
almost all of the other questions were answered with a button added after scrollPane in BorderLayout.South , which is not what i want.
thanks
updated the picture to better represent it, the button should appear inside the scrollPane just after the last row.
Read more about different LayoutManager's. Use proper manager or combination of them.
Here is simple example:
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
public class TestFrame extends JFrame {
public TestFrame() {
init();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private void init() {
JTable t = new JTable(3,3);
add(new JScrollPane(t));
JPanel btns = new JPanel(new FlowLayout(FlowLayout.LEFT,0,0));
JButton jButton = new JButton("here");
btns.add(jButton);
add(btns,BorderLayout.SOUTH);
}
public static void main(String args[]) {
new TestFrame();
}
}
EDIT:
Try to use next example:
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
public class TestFrame extends JFrame {
public TestFrame() {
init();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private void init() {
JTable t = new JTable(new DefaultTableModel(3,3){
#Override
public boolean isCellEditable(int row, int column) {
if(row == getRowCount()-1 && column != 0){
return false;
}
return super.isCellEditable(row, column);
}
});
t.setGridColor(t.getTableHeader().getBackground());
add(new JScrollPane(t));
for(int i=0;i<3;i++)
t.getColumnModel().getColumn(i).setCellRenderer(getDummyRenderer());
t.getColumnModel().getColumn(0).setCellEditor(getDummyEditor());
}
private TableCellEditor getDummyEditor() {
return new DummyEditor();
}
private TableCellRenderer getDummyRenderer() {
return new DefaultTableCellRenderer() {
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
if(row == table.getRowCount()-1){
if(column == 0){
return new JButton("dummy");
} else {
JLabel jLabel = new JLabel("");
jLabel.setOpaque(true);
jLabel.setBorder(null);
return jLabel;
}
} else {
Component tableCellRendererComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
return tableCellRendererComponent;
}
}
};
}
public static void main(String args[]) {
new TestFrame();
}
private class DummyEditor extends DefaultCellEditor implements TableCellEditor{
private JButton btn = new JButton("dummy");
public DummyEditor(){
super(new JTextField());
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("clicked");
}
});
}
#Override
public Component getTableCellEditorComponent(JTable table,
Object value, boolean isSelected, int row, int column) {
return row == table.getRowCount()-1 ? btn : super.getTableCellEditorComponent(table, value, isSelected, row, column);
}
}
}
Related
I have written a renderer for JComboBoxes to replace the milky colour when in
disabled state by a more legible one.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.*;
public class RendererDemo extends JFrame {
public static final long serialVersionUID = 100L;
String[] items= {"one", "two", "three"};
public RendererDemo() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 100);
JComboBox<Object> cmb = new JComboBox<>(items);
// JComboBox<String> cmb = new JComboBox<>(items);
cmb.setRenderer(new DisabledDarkTextRenderer(cmb));
add(cmb, BorderLayout.NORTH);
JCheckBox check = new JCheckBox("Combo enabled", true);
add(check, BorderLayout.SOUTH);
check.addActionListener(e -> cmb.setEnabled(check.isSelected()));
setVisible(true);
}
static public void main(String args[]) {
EventQueue.invokeLater(RendererDemo::new);
}
class DisabledDarkTextRenderer extends DefaultListCellRenderer {
public static final long serialVersionUID = 50378L;
JComboBox<Object> cmb;
ListCellRenderer<Object> origRenderer;
Object defaultResource= UIManager.get("ComboBox.disabledForeground");
public DisabledDarkTextRenderer(JComboBox<Object> cmb) {
this.cmb= cmb;
origRenderer= cmb.getRenderer();
}
public Component getListCellRendererComponent(JList<?> list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
Component orig= origRenderer.getListCellRendererComponent(list, value,
index, isSelected, cellHasFocus);
if (cmb.isEnabled())
UIManager.put("ComboBox.disabledForeground", defaultResource);
else
UIManager.put("ComboBox.disabledForeground", new ColorUIResource
(Color.GRAY));
return orig;
}
}
}
However, when I declare the JComboBox as being of type <String>, the code fails to compile due to "incompatible types: ListCellRenderer<CAP#1> cannot be converted to ListCellRenderer<Object> ..."
So I tried to make the renderer more general. From all my attempts to modify the
generic type, the following version is the one with only one error left. It's again the above mentioned error, only that it reads now: "incompatible types: JList<CAP#1> cannot be converted to JList<? extends CAP#2> ...".
How to fix this?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.*;
public class RendererDemo2 extends JFrame {
public static final long serialVersionUID = 100L;
String[] items= {"one", "two", "three"};
public RendererDemo2() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 100);
JComboBox<String> cmb = new JComboBox<>(items);
cmb.setRenderer(new DisabledDarkTextRenderer2(cmb));
add(cmb, BorderLayout.NORTH);
JCheckBox check = new JCheckBox("Combo enabled", true);
add(check, BorderLayout.SOUTH);
check.addActionListener(e -> cmb.setEnabled(check.isSelected()));
setVisible(true);
}
static public void main(String args[]) {
EventQueue.invokeLater(RendererDemo2::new);
}
class DisabledDarkTextRenderer2 extends DefaultListCellRenderer {
public static final long serialVersionUID = 50379L;
JComboBox<?> cmb;
ListCellRenderer<?> origRenderer;
Object defaultResource= UIManager.get("ComboBox.disabledForeground");
public DisabledDarkTextRenderer2(JComboBox<?> cmb) {
this.cmb= cmb;
origRenderer= cmb.getRenderer();
}
public Component getListCellRendererComponent(JList<?> list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
Component orig= origRenderer.getListCellRendererComponent(list, value,
index, isSelected, cellHasFocus);
if (cmb.isEnabled())
UIManager.put("ComboBox.disabledForeground", defaultResource);
else
UIManager.put("ComboBox.disabledForeground", new ColorUIResource
(Color.GRAY));
return orig;
}
}
}
Following MadProgrammer's advice my renderer now looks like this:
class DisabledDarkTextRenderer2<T> extends DefaultListCellRenderer {
public static final long serialVersionUID = 50379L;
JComboBox<T> cmb;
ListCellRenderer<?> origRenderer;
Object defaultResource= UIManager.get("ComboBox.disabledForeground");
public DisabledDarkTextRenderer2(JComboBox<T> cmb) {
this.cmb= cmb;
origRenderer= cmb.getRenderer();
}
public Component getListCellRendererComponent(JList<?> list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
Component orig= origRenderer.getListCellRendererComponent(list, value,
index, isSelected, cellHasFocus);
if (cmb.isEnabled())
UIManager.put("ComboBox.disabledForeground", defaultResource);
else
UIManager.put("ComboBox.disabledForeground", new ColorUIResource
(Color.GRAY));
return orig;
}
}
but the compile time error persists.
I am using this renderer for combos which change their state at runtime. So I am free from caring in each program to set the colour when needed, as the renderer is doing this automatically for me.
You need to "genericfy" your DisabledDarkTextRenderer2, as an example...
class DisabledDarkTextRenderer2<T> implements ListCellRenderer<T> {
public static final long serialVersionUID = 50379L;
private JComboBox<T> combobox;
private ListCellRenderer<? super T> origRenderer;
public DisabledDarkTextRenderer2(JComboBox<T> combobox) {
this.combobox = combobox;
this.origRenderer = combobox.getRenderer();
}
public static <T> void install(JComboBox<T> combobox) {
combobox.setRenderer(new DisabledDarkTextRenderer2<T>(combobox));
}
#Override
public Component getListCellRendererComponent(JList<? extends T> list, T value, int index, boolean isSelected, boolean cellHasFocus) {
Component orig = origRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (combobox.isEnabled()) {
orig.setForeground(null);
} else {
orig.setForeground(Color.GRAY);
}
return orig;
}
}
Now, having said that, I would discourage you from using UIManager within the renderer, as this will effect ALL components rendered after the change, which may or may not be your desired goal, if so, then just set the value at the start of the application.
The following example will make the default disabled foreground color RED for all comboboxes without you having to do anything else at all.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.ColorUIResource;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
UIManager.put("ComboBox.disabledForeground", new ColorUIResource(Color.RED));
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(32, 32, 32, 32));
JComboBox<String> cmb = new JComboBox<>(new String[]{"Hello", "Goodbye"});
cmb.setSelectedIndex(0);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(cmb, gbc);
JButton toggle = new JButton("Toggle");
toggle.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cmb.setEnabled(!cmb.isEnabled());
}
});
add(toggle, gbc);
}
}
}
I have a JTable in which one column (Column 1) is a JComboBox that allows options to be made from a list, and to which new options can be entered. MWE:
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.TableColumn;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.HashSet;
public class ComboTableDemo extends JPanel {
public ComboTableDemo() {
super(new GridLayout(1,0));
final String[] headings = {"Name", "Option"};
final String string1 = "Foo";
final String string2 = "Bar";
Object[][] data = {
{"Albert", string1},
{"Bob", null},
{"Clare", null},
{"David", null}
};
final JTable table = new JTable(data, headings);
table.setPreferredScrollableViewportSize(new Dimension(300, 100));
table.setFillsViewportHeight(true);
final String[] optionsInit = new String[] {string1, string2};
HashSet<String> options = new HashSet<String>(Arrays.asList(optionsInit));
JComboBox<String> optionsCombo = new JComboBox<String>(optionsInit);
optionsCombo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ev) {
String newSelection = (String)optionsCombo.getSelectedItem();
if(!options.contains(newSelection)) {
options.add(newSelection);
optionsCombo.addItem(newSelection);
}
}
});
optionsCombo.setEditable(true);
TableColumn column = table.getColumnModel().getColumn(1);
column.setCellEditor(new DefaultCellEditor(optionsCombo));
add(new JScrollPane(table));
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("ComboTableDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ComboTableDemo pane = new ComboTableDemo();
pane.setOpaque(true);
frame.setContentPane(pane);
frame.pack();
frame.setVisible(true);
}
});
}
}
In the table it isn't obvious that the user can and should enter values for the empty entries, so I'd like to include placeholder text to make this clear. I've seen elsewhere that a custom ListCellRenderer can be provided with setRenderer in the case of uneditable combos, but in the case of editable combos (as explained in the tutorial) it appears that a ComboBoxEditor must be supplied using setEditor. Is there a simple implementation for this, or perhaps even a better way to achieve the same ends?
Actually, input components within a JTable present a special case, since they are only active when the cell is edited. So as well as setting the editor to control how a value is edited, you also need to change the renderer to control how the chosen value is presented, with
// As before:
TableColumn column = table.getColumnModel().getColumn(1);
column.setCellEditor(new DefaultCellEditor(optionsCombo));
// Additional line to set renderer:
column.setCellRenderer(new PlaceholderRenderer("<choose or add option>"));
Here the PlaceholderRenderer should be a TableCellRenderer implementation that displays the placeholder string when no value is selected. E.g.:
import java.awt.Component;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
public class PlaceholderRenderer extends DefaultTableCellRenderer {
final private String placeholder;
public PlaceholderRenderer(String placeholder) {
super();
this.placeholder = placeholder;
}
#Override
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column) {
if ((value == null) || (value.equals(""))) {
return super.getTableCellRendererComponent(table, this.placeholder, isSelected, hasFocus, row, column);
} else {
return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
}
}
I am currently working on a JComboBox component where I want to have a JTable inside of a ComboBox for a drop down selection. I have extended the ListCellRenderer and I have the table inside of the popup.
I want to present it two different ways. The first as a label of a bound column of the selected row when the popup is not visible. The second is to show the table with a JScrollPane when the popup is visible.
Unfortunately, When I do this the popup is being shrunk to the row height of the list which only leaves room for the columns of the table.
If I just use the scrollpane I can see the full table but then when the popup is not visible I see the table inside the combobox which is not what I want.
How can I set the height such that the table can fit while still being able to show the label only when the popup is not visible?
Below is a minimal example that compiles and runs:
import java.awt.Color;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListCellRenderer;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
public class TableComboBoxMain {
public static void main(String[] args) {
JTableComboBox<Employee> combo = new JTableComboBox<>();
combo.addItem(new Employee("April",3));
//combo.setMaximumRowCount(10);
combo.setRenderer(new TableListCellRenderer(combo));
JFrame frame = new JFrame("Test Table Combo Box");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(combo);
frame.pack();
frame.setVisible(true);
}
public static class Employee{
String name;
int nr;
public Employee(String name, int number ){
this.name =name;
this.nr = number;
}
}
public static class JTableComboBox<E> extends JComboBox<E> implements ListSelectionListener{
#Override
public void valueChanged(ListSelectionEvent e) {System.out.println("Row selected"+e.getFirstIndex());
this.setSelectedIndex(e.getFirstIndex());
}
}
public static class TableListCellRenderer extends JScrollPane implements ListCellRenderer{
JTable table;
JTableComboBox combo;
boolean mouseListenerAdded;
public TableListCellRenderer(JTableComboBox combo){
this.combo=combo;
DefaultTableModel model = new DefaultTableModel();
String[] cols1 = new String[]{"1","2","3","4"};
String[] cols2 = new String[]{"Mark","John","April","Mary"};
model.addColumn("Nr", cols1);model.addColumn("Name",cols2);
table = new JTable(model);table.setShowGrid(true);table.setGridColor(Color.gray);
this.getViewport().add(table);
}
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if(!mouseListenerAdded){
list.addMouseListener(this.getListener());
mouseListenerAdded = true;
}//If this is uncommented then the BasicComboPopup size is always no more than 1 row?!!
if(!combo.isPopupVisible()){
Employee emp = (Employee) value;
return new JLabel(emp.name);
}else
return this;
}
public MouseAdapter getListener(){
MouseAdapter adapter = new MouseAdapter(){
#Override
public void mousePressed(MouseEvent e) {
if(combo.isPopupVisible()){
System.out.println("MouseClicked over list");
int row = table.rowAtPoint(e.getPoint());
if(row>0){
table.setRowSelectionInterval(row-1, row-1);
ListDataEvent event = new ListDataEvent(combo,ListDataEvent.CONTENTS_CHANGED,row-1,row-1);
combo.contentsChanged(event);
}
}
}
};
return adapter;
}
}
}
So I found a solution to the problem. I am not finished yet because there are a few interactions I still want to have:
I want to be able to move columns
I want to be able have popup menu for columns
I want to be able to scroll vertically with the mouse
I wanted to post the solution in case anyone else wants a starting point example. I will update my answer as I solve these additional issues.
Below is the test class:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.table.DefaultTableModel;
public class TableComboBoxMain {
public static void main(String[] args) {
JTableComboBox<String> combo = new JTableComboBox<>();
combo.addItem("");
BasicComboBoxUI ui = new BasicComboBoxUI(){
#Override
protected ComboPopup createPopup() {
return new BasicComboPopup( comboBox ){
#Override
protected int getPopupHeightForRowCount(int maxRowCount) {
return 100;
}
#Override
protected JScrollPane createScroller() {
JScrollPane sp = new JScrollPane( list,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED );
sp.getHorizontalScrollBar().setFocusable( false );
return sp;
}
};
}
};
combo.setUI(ui);
combo.setRenderer(new TableListCellRenderer(combo));
JFrame frame = new JFrame("Test Table Combo Box");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(combo);
frame.pack();
frame.setVisible(true);
}
public static class Employee{
String name;
int nr;
public Employee(String name, int number ){
this.name =name;
this.nr = number;
}
}
public static class JTableComboBox<E> extends JComboBox<E> implements ListSelectionListener{
#Override
public void valueChanged(ListSelectionEvent e) {
System.out.println("Row selected"+e.getFirstIndex());
this.setSelectedIndex(e.getFirstIndex());
}
}
public static class TableListCellRenderer extends DefaultListCellRenderer{
JTable table;
JTableComboBox combo;
JPanel renderer = new JPanel();
JPanel colHeader = new JPanel();
JScrollPane scroll = new JScrollPane();
boolean mouseListenerAdded;
public TableListCellRenderer(JTableComboBox combo){
this.combo=combo;
DefaultTableModel model = new DefaultTableModel();
String[] cols1 = new String[]{"1","2","3","4","5","6"};
String[] cols2 = new String[]{"Mark","John","April","Mary","Joe","Jack"};
model.addColumn("Nr", cols1);model.addColumn("Name",cols2);
table = new JTable(model);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
colHeader.add(table.getTableHeader());
renderer.setLayout(new BorderLayout());
renderer.add(colHeader, BorderLayout.NORTH);
renderer.add(table,BorderLayout.CENTER);
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
scroll.getViewport().add(table);
}
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
list.setFixedCellHeight(table.getRowHeight()*(table.getRowCount()+1)+10);
list.setFixedCellWidth(table.getPreferredSize().width);
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if(!mouseListenerAdded){
list.addMouseListener(this.getListener());
mouseListenerAdded = true;
}
if(!combo.isPopupVisible()){
label.setText("Select...");
if(table.getSelectedRowCount()>0)
label.setText(""+table.getModel().getValueAt(table.getSelectedRow(),1));
return label;
}
return scroll;
}
public MouseAdapter getListener(){
MouseAdapter adapter = new MouseAdapter(){
#Override
public void mousePressed(MouseEvent e) {
if(combo.isPopupVisible()){
System.out.println("MouseClicked over list");
int row = table.rowAtPoint(e.getPoint());
if(row>0){
table.setRowSelectionInterval(row-1, row-1);
ListDataEvent event = new ListDataEvent(combo,ListDataEvent.CONTENTS_CHANGED,row-1,row-1);
combo.contentsChanged(event);
}
}
}
};
return adapter;
}
}
}
The key parts of the solution is that in order to have the table and the features of a table that you would want in a popup is you need to override the UI. Specifically you need to override the createPopup on BasicComboBoxUI, getPopupHeightForRowCount and createScroller on BasicComboPopup. Lastly, when implementing getListCellRenderingComponent you have set a fixed height and width that matches the tables preferred height and width. This will allow the main scroller of the popup to act as the scrollpane for the table.
I'm stuck for two days on a piece of code, the cells of my table are non-editable default and I want to editable when I click on the edit button, but I can not run this code.
Thank you in advance for your help.
table = new JTable(model) {
public boolean isCellEditable(int row, int column) {
return false;
}
};
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
sorter = new TableRowSorter < TableModel > (model);
table.setPreferredScrollableViewportSize(new Dimension(560, 200));
JScrollPane scrollpane = new JScrollPane((table));
table.setRowSorter(sorter);
add(ScrollPane);
scrollPane.setBounds(10, 180, 560, 200);
table.setRowHeight(28);
table.setBackground(Color.DARK_GRAY);
table.setForeground(Color.WHITE);
modifier.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if ((JButton) e.getSource() == change) {
boolean isCellEditable(int row, int column) {
return true;
}
}
}
});
Put enabled cell (row, col) to a set and make your table use that set to check if cell is enabled.
package javaapplication33;
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JLabel;
import javax.swing.ListSelectionModel;
import javax.swing.JScrollPane;
import javax.swing.Action;
import javax.swing.AbstractAction;
import javax.swing.JTextField;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.util.Set;
import java.util.HashSet;
import java.util.Collection;
import java.awt.EventQueue;
import javax.swing.Box;
import java.awt.Color;
import javax.swing.BoxLayout;
import javax.swing.table.AbstractTableModel;
public class JavaApplication33
{
public static void main(String[] args)
{
Runnable r = new Runnable()
{
#Override
public void run()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel(new BorderLayout());
p.setPreferredSize(new Dimension(300,300));
final Set<Point> erc = new HashSet<>(); //this set contains (row,cell) pairs of editable cells
//put some data to the table
final JTable table = new JTable(new String[][]
{{"a", "aa"},
{"b", "bb"},
{"c", "cc"}},
new String[]{"col1", "col2"}) //name columns
{
Collection<Point> editableRowsCells = erc;
Collection<Point> getEditableRowsCells()
{
return editableRowsCells;
}
public boolean isCellEditable(int row, int column)
{
return getEditableRowsCells()
.contains(new Point(row, column));
}
};
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane scrollpane = new JScrollPane(table);
p.add(scrollpane, BorderLayout.CENTER);
table.setRowHeight(28);
table.setBackground(Color.DARK_GRAY);
table.setForeground(Color.WHITE);
final JTextField xf = new JTextField();
final JTextField yf = new JTextField();
Action enableAction = new AbstractAction("Enable")
{
#Override
public void actionPerformed(ActionEvent e)
{
int row = Integer.valueOf(xf.getText());
int col = Integer.valueOf(yf.getText());
erc.add(new Point(row,col));
((AbstractTableModel) table.getModel())
.fireTableCellUpdated(row, col);
}
};
JButton enableButton = new JButton();
enableButton.setAction(enableAction);
Box b = new Box(BoxLayout.X_AXIS);
b.add(new JLabel("Row"));
b.add(xf);
b.add(new JLabel("Col"));
b.add(yf);
b.add(enableButton);
p.add(b, BorderLayout.NORTH);
f.add(p);
f.pack();
f.setVisible(true);
}
};
EventQueue.invokeLater(r);
}
}
This is the output:
Update: to make all cells editable
package javaapplication33;
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.JScrollPane;
import javax.swing.Action;
import javax.swing.AbstractAction;
import java.awt.event.ActionEvent;
import java.awt.EventQueue;
import java.awt.Color;
import javax.swing.table.AbstractTableModel;
public class JavaApplication33
{
public static class Toggle
{
Boolean val;
public Toggle(Boolean val)
{
this.val = val;
}
public void setVal(Boolean val)
{
this.val = val;
}
public Boolean getVal()
{
return val;
}
public void toggle()
{
setVal(!getVal());
}
}
public static void main(String[] args)
{
Runnable r = new Runnable()
{
#Override
public void run()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel(new BorderLayout());
p.setPreferredSize(new Dimension(300, 300));
final Toggle editable = new Toggle(false);
final JTable table = new JTable(
new String[][]{
{"a", "aa"},
{"b", "bb"},
{"c", "cc"}},
new String[] {"col1", "col2"}) //name columns
{
public boolean isCellEditable(int row, int column)
{
return editable.getVal(); //request current value
}
};
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane scrollpane = new JScrollPane(table);
p.add(scrollpane, BorderLayout.CENTER);
table.setRowHeight(28);
table.setBackground(Color.DARK_GRAY);
table.setForeground(Color.WHITE);
Action enableAction = new AbstractAction("Toggle editable")
{
#Override
public void actionPerformed(ActionEvent e)
{
editable.toggle(); //toggle current value
((AbstractTableModel) table.getModel()).fireTableStructureChanged();
}
};
JButton enableButton = new JButton();
enableButton.setAction(enableAction);
p.add(enableButton, BorderLayout.NORTH);
f.add(p);
f.pack();
f.setVisible(true);
}
};
EventQueue.invokeLater(r);
}
}
Output:
Here my code :
table = new JTable(model){
public boolean isCellEditable(int row, int column) {
return false;
}
};
modifier.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if ((JButton)e.getSource()== modifier)
{
public boolean isCellEditable(int row, int column) {
return true;
}
}
}
});
Designing a custom JTable I already know that DefaultCellEditor admits a JComboBox in his constructor. This JComboBox, when clicked to display the list items, shows above the other cells of the JTable. The problem is that I need a more sophisticated behavior as what JComboBox offers, so that I've implemented a JTextField with a JList and a JButton, when the JButton gets clicked (or the user enters text in the JTextField) the elements in the JList become visible. This 3 elements are in a JPanel. When I try to use this panel as a cell editor (extending AbtractCellEditor and implementing TableCellEditor) the elements in the list show inside the editing cell but I cannot mimic the behavior of the DefaultCellEditor with the combo so that the list elements show above the JTable.
Here I define the custom cell editor: (very short version);
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.TableCellEditor;
public class CustomCellEditor extends AbstractCellEditor implements TableCellEditor {
private JList list;
private JButton button;
private JTextField editor;
private JPanel mainPanel;
public CustomCellEditor() {
list = new JList(new String[] { "One", "Two", "Three" });
editor = new JTextField();
button = new JButton("Click me ");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
list.setVisible(!list.isVisible());
}
});
JPanel auxPanel = new JPanel(new BorderLayout());
auxPanel.add(editor, BorderLayout.CENTER);
auxPanel.add(button, BorderLayout.EAST);
mainPanel = new JPanel(new BorderLayout());
mainPanel.add(auxPanel, BorderLayout.NORTH);
mainPanel.add(list, BorderLayout.CENTER);
}
#Override
public Object getCellEditorValue() {
return editor.getText();
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
return mainPanel;
}
}
And this a main program with a jtable with this panel as a cell editor:
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
public class CustomTable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
CustomTable instance = new CustomTable();
instance.createAndShowUI();
}
});
}
private void createAndShowUI() {
JTable table = new JTable(new CustomTableModel());
//So that I can see the contents of the list when edited
table.setRowHeight(60);
TableColumn editableColumn = table.getColumnModel().getColumn(0);
editableColumn.setPreferredWidth(250);
editableColumn.setCellEditor(new CustomCellEditor());
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(table, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
private class CustomTableModel extends AbstractTableModel {
private final String[] columnNames = {"Editable column", "Other column"};
private final Object[][] data = {
{"Ricardo", "Mr."},
{"Josefina", "Ms."}
};
#Override
public int getRowCount() {
return data.length;
}
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return data[rowIndex][columnIndex];
}
#Override
public boolean isCellEditable(int row, int col) {
if (0 == col)
return true;
return false;
}
#Override
public void setValueAt(Object value, int row, int col) {
data[row][col] = value;
fireTableCellUpdated(row, col);
}
}
}
Can someone help me?
Thanks!!
Using a JComboBox as a table editor relies on it being a button that displays a popup component when pressed. As you have several components in a cell, consider these alternatives:
Add the components to a panel, as shown here for a group of JRadioButton instances.
Add the components to a modal dialog, as shown here using JDialog.