JTable Rendering with checkbox - java

Is there anyone know how I can add a checkbox in this code:
String data[][]={
{"Apple","Banana","Mango"}, {"Apple","Banana","Mango"}, {"Apple","Banana","Mango"}
};
String column[]={"Fruits","Fruits","Fruits"};
table=new JTable(new DefaultTableModel(data, column)){
private Border outside = new MatteBorder(1, 0, 1, 0, Color.RED);
private Border inside = new EmptyBorder(0, 1, 0, 1);
private Border highlight = new CompoundBorder(outside, inside);
public Component prepareRenderer(TableCellRenderer renderer, int row, int column)
{
Component c = super.prepareRenderer(renderer, row, column);
JComponent jc = (JComponent)c;
// Add a border to the selected row
if (isRowSelected(row))
jc.setBorder( highlight );
return c;
}
};
jScrollPane1.setViewportView(table);
I just want to add checkboxes so that if I check a checkbox it will highlight and all checked checkboxes will be highlighted. Thank You in advance for helping me!

Here is pseudo code, I found somewhere in my repository. Use it according to your use.
import java.awt.Color;
import java.awt.Component;
import javax.swing.*;
import javax.swing.table.*;
public class TableCheckBoxHighLight extends JFrame {
private static final long serialVersionUID = 1L;
private JTable table;
public TableCheckBoxHighLight() {
Object[] columnNames = { "Col1", "Col2", "Select" };
Object[][] data = {
{ "Item1", "123", false },
{ "Item2", "345", false },
{ "Item3", "678", false },
{ "Item4", "901", false }
};
DefaultTableModel model = new DefaultTableModel(data, columnNames);
table = new JTable(model) {
private static final long serialVersionUID = 1L;
#SuppressWarnings("unchecked")
#Override
public Class getColumnClass(int column) {
switch (column) {
case 0:
return String.class;
case 1:
return String.class;
default:
return Boolean.class;
}
}
#Override
public Component prepareRenderer(TableCellRenderer renderer,
int row, int col) {
Component c = super.prepareRenderer(renderer, row, col);
int[] selCols = table.getSelectedColumns();
table.setSelectionBackground(Color.GREEN);
for (int i : selCols)
c.setBackground(Color.RED);
return c;
}
};
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane(table);
getContentPane().add(scrollPane);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TableCheckBoxHighLight frame = new TableCheckBoxHighLight();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocation(150, 150);
frame.setVisible(true);
}
});
}
}

Related

How to do a selection in cascade of JCombobox choices in a JTable?

I have a JTable with numbers 1,2,3 as 1st column and the number as text in the 2nd column which can be chosen with a JCombobox. For example 1 can be represented as "1", ONE, FIRST or ONCE in the 2nd column. When I make a choice, all the comboboxes of the rows below have to be updated in cascade with the text of the same nature. So if I choose ONCE, the comboboxes of the rows below should be updated to TWICE, THRICE. If I choose FIRST, the comboboxes of the rows below should be updated to SECOND, THIRD. And so on..
At first it looks like it's working but whenever I click somewhere else on the JTable, the combobox is updated with the value of the last row. For example, if I choose ONCE in the 1st row, at first it will update the other rows to TWICE and THRICE. Then if I click on any row, the combobox selection will be updated to THRICE on the 1st row. Next time I click, the 2nd row is updated to THRICE.
What am I doing wrong here?
The combobox cell editor:
public class NumberChoiceEditor extends DefaultCellEditor {
private static final long serialVersionUID = 1L;
private String numberAsText;
private static final String[][] NUMBERS = { { "1", "ONE", "FIRST", "ONCE" }, { "2", "TWO", "SECOND", "TWICE" }, { "3", "THREE", "THIRD", "THRICE" } };
public NumberChoiceEditor() {
super(new JComboBox<>());
}
#Override
public Object getCellEditorValue() {
return numberAsText;
}
#Override
public Component getTableCellEditorComponent(final JTable table, final Object value, final boolean isSelected, final int row, final int column) {
if (value instanceof String) {
numberAsText = (String) value;
}
JComboBox<String> numberTextChoice = new JComboBox<>(NUMBERS[row]);
numberTextChoice.setSelectedItem(numberAsText);
numberTextChoice.addActionListener(e -> {
numberAsText = (String) numberTextChoice.getSelectedItem();
int nextRow = row + 1;
if (nextRow < NUMBERS.length) {
String numberText = (String) table.getValueAt(nextRow, 1);
JComboBox<String> nextRowChoices = (JComboBox<String>) getTableCellEditorComponent(table, numberText, isSelected, nextRow, column);
nextRowChoices.setSelectedIndex(numberTextChoice.getSelectedIndex());
table.setValueAt(nextRowChoices.getSelectedItem(), nextRow, 1);
}
});
return numberTextChoice;
}
}
The main class with the frame:
public class NumberTable {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JTable table = new JTable(new Object[][] { { 1, "1" }, { 2, "2" }, { 3, "3" } }, new Object[] { "Number", "Number Text" });
table.getColumn("Number Text").setCellEditor(new NumberChoiceEditor());
table.setRowHeight(25);
JFrame frame = new JFrame("Number Table");
frame.add(new JScrollPane(table));
frame.setLocationRelativeTo(null);
frame.setSize(600, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
}
I would use a TableModelListener. When the value is changed via the JComboBox editor, I would adjust the TableModel accordingly.
(Notes after the code.)
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
public class NumTable implements Runnable, TableModelListener {
private boolean adjusting;
private JFrame frame;
private JTable table;
#Override
public void run() {
createGui();
}
#Override
public void tableChanged(TableModelEvent event) {
if (!adjusting) {
adjusting = true;
int col = event.getColumn();
if (col == 1) {
NumChoiceEd editor = (NumChoiceEd) table.getColumnModel().getColumn(1).getCellEditor();
int row = event.getFirstRow();
JComboBox<String> combo = editor.getCombo(row);
if (combo != null) {
int ndx = combo.getSelectedIndex();
if (ndx >= 0) {
int rows = table.getRowCount();
for (int i = 0; i < rows; i++) {
combo = editor.getCombo(i);
String val = combo.getModel().getElementAt(ndx);
table.setValueAt(val, i, col);
}
}
}
}
adjusting = false;
}
}
private void createGui() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createTable(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JScrollPane createTable() {
Object[][] data = new Object[][]{{1, "1"}, {2, "2"}, {3, "3"}};
Object[] columnNames = new Object[]{"Number", "Number Text"};
DefaultTableModel model = new DefaultTableModel(data, columnNames);
model.addTableModelListener(this);
table = new JTable(model);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
TableColumnModel tcm = table.getColumnModel();
TableColumn column = tcm.getColumn(1);
column.setCellEditor(new NumChoiceEd());
JScrollPane scrollPane = new JScrollPane(table);
return scrollPane;
}
public static void main(String[] args) {
EventQueue.invokeLater(new NumTable());
}
}
class NumChoiceEd extends DefaultCellEditor {
private static final long serialVersionUID = 1L;
private JComboBox<String> oneCombo;
private JComboBox<String> twoCombo;
private JComboBox<String> threeCombo;
public NumChoiceEd() {
super(new JComboBox<>());
oneCombo = new JComboBox<>(new String[]{"1", "ONE", "FIRST", "ONCE"});
twoCombo = new JComboBox<>(new String[]{"2", "TWO", "SECOND", "TWICE"});
threeCombo = new JComboBox<>(new String[]{"3", "THREE", "THIRD", "THRICE"});
}
public JComboBox<String> getCombo(int row) {
switch (row) {
case 0:
return oneCombo;
case 1:
return twoCombo;
case 2:
return threeCombo;
default:
return null;
}
}
#Override
public Component getTableCellEditorComponent(final JTable table,
final Object value,
final boolean isSelected,
final int row,
final int column) {
if (column == 1) {
switch (row) {
case 0:
return oneCombo;
case 1:
return twoCombo;
case 2:
return threeCombo;
default:
return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}
}
else {
return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}
}
}
Rather than create a new JComboBox each time method getTableCellEditorComponent is called, I initially create all three JComboBoxes and return the relevant one.
You don't need to override method getCellEditorValue because the superclass method (in class DefaultCellEditor) will return the correct value.
Once the user changes the value (and closes the table cell editor), method tableChanged is invoked. In that method, I get the index of the value that was selected from the JComboBox and then I go through all the rows in the JTable and get the value at that index in the JComboBox for each row and set the JTable value to that value.
Because I change the TableModel in method tableChanged, that will cause the method to be called again. In order to prevent the recursive call, I use the adjusting flag.
In the below screen capture, I have selected a value from the JComboBox but I have not yet closed the editor. If I navigate to a different cell in the JTable that will close the editor and then all the displayed data will change. Note that if you navigate to column Number Text in a different row, you may not see the change since that will immediately open the JComboBox editor for the cell that you navigated to.
After I close the editor, the table looks as in the below screen capture.
Note that there is a lot of blank space in the above screen captures since the default dimensions of JTable are quite large but the space required to display the data (in this case) is small. One way to make the JTable smaller (in this case) would be to change the preferred size of the JScrollPane.
EDIT
In response to the question you asked in your comment, namely
is it possible to update on the combobox value change
Yes, it is possible. You add an ActionListener to each JComboBox that simply calls method stopCellEditing. Here is the above code, modified to include the ActionListener. The only changes are in class NumChoiceEd.
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionListener;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
public class NumTable implements Runnable, TableModelListener {
private boolean adjusting;
private JFrame frame;
private JTable table;
#Override
public void run() {
createGui();
}
#Override
public void tableChanged(TableModelEvent event) {
if (!adjusting) {
adjusting = true;
int col = event.getColumn();
if (col == 1) {
NumChoiceEd editor = (NumChoiceEd) table.getColumnModel().getColumn(1).getCellEditor();
int row = event.getFirstRow();
JComboBox<String> combo = editor.getCombo(row);
if (combo != null) {
int ndx = combo.getSelectedIndex();
if (ndx >= 0) {
int rows = table.getRowCount();
for (int i = 0; i < rows; i++) {
combo = editor.getCombo(i);
String val = combo.getModel().getElementAt(ndx);
table.setValueAt(val, i, col);
}
}
}
}
adjusting = false;
}
}
private void createGui() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createTable(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JScrollPane createTable() {
Object[][] data = new Object[][]{{1, "1"}, {2, "2"}, {3, "3"}};
Object[] columnNames = new Object[]{"Number", "Number Text"};
DefaultTableModel model = new DefaultTableModel(data, columnNames);
model.addTableModelListener(this);
table = new JTable(model);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
TableColumnModel tcm = table.getColumnModel();
TableColumn column = tcm.getColumn(1);
column.setCellEditor(new NumChoiceEd());
JScrollPane scrollPane = new JScrollPane(table);
return scrollPane;
}
public static void main(String[] args) {
EventQueue.invokeLater(new NumTable());
}
}
class NumChoiceEd extends DefaultCellEditor {
private static final long serialVersionUID = 1L;
private JComboBox<String> oneCombo;
private JComboBox<String> twoCombo;
private JComboBox<String> threeCombo;
public NumChoiceEd() {
super(new JComboBox<>());
ActionListener al = event -> NumChoiceEd.this.stopCellEditing(); // ADDED THIS LINE
oneCombo = new JComboBox<>(new String[]{"1", "ONE", "FIRST", "ONCE"});
oneCombo.addActionListener(al); // ADDED THIS LINE
twoCombo = new JComboBox<>(new String[]{"2", "TWO", "SECOND", "TWICE"});
twoCombo.addActionListener(al); // ADDED THIS LINE
threeCombo = new JComboBox<>(new String[]{"3", "THREE", "THIRD", "THRICE"});
threeCombo.addActionListener(al); // ADDED THIS LINE
}
public JComboBox<String> getCombo(int row) {
switch (row) {
case 0:
return oneCombo;
case 1:
return twoCombo;
case 2:
return threeCombo;
default:
return null;
}
}
#Override
public Component getTableCellEditorComponent(final JTable table,
final Object value,
final boolean isSelected,
final int row,
final int column) {
if (column == 1) {
switch (row) {
case 0:
return oneCombo;
case 1:
return twoCombo;
case 2:
return threeCombo;
default:
return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}
}
else {
return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}
}
}

How to put different JComboBox in each row of JTable dinamically

I would like to create a JTable in my application.
The table will contain two coumns, the first is the name of a software module.
The second column needs to contain a JComboBox with the revision numbers of the software module in the first column.
Is it even possible or I need to find another way to do this? (For example: put a button to the cell and choose the revision from a popup window)
Thank you in advance!
Here is one possible implementation to use a DefaultComboBoxModel<Integer> as the table column's data type:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
public class EditableComboCellEditorTest {
private JComponent makeUI() {
String[] columnNames = {"version", "revision"};
Object[][] data = {
{"1.7.0", makeModel(76, 79, 80)},
{"1.8.0", makeModel(91, 92, 101, 102)},
};
TableModel model = new DefaultTableModel(data, columnNames) {
#Override public Class<?> getColumnClass(int column) {
return column == 1 ? DefaultComboBoxModel.class : String.class;
}
};
JTable table = new JTable(model);
table.setRowHeight(32);
table.setAutoCreateRowSorter(true);
TableColumn col = table.getColumnModel().getColumn(1);
col.setCellRenderer(new ComboCellRenderer());
col.setCellEditor(new ComboCellEditor());
return new JScrollPane(table);
}
private static DefaultComboBoxModel<Integer> makeModel(Integer... items) {
return new DefaultComboBoxModel<Integer>(items) {
#Override public String toString() {
return Objects.toString(getSelectedItem(), "");
}
};
}
public static void main(String... args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new EditableComboCellEditorTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
class ComboCellRenderer implements TableCellRenderer {
private final JPanel p = new JPanel(new GridBagLayout());
private final JComboBox<Integer> cb = new JComboBox<>();
#Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
p.removeAll();
p.setOpaque(false);
p.add(cb);
cb.setEditable(true);
cb.removeAllItems();
if (value instanceof DefaultComboBoxModel) {
DefaultComboBoxModel m = (DefaultComboBoxModel) value;
Object o = m.getSelectedItem();
if (o instanceof Integer) {
cb.addItem((Integer) o);
}
}
return p;
}
}
//https://java-swing-tips.blogspot.jp/2016/08/use-editable-jcombobox-as.html
class ComboCellEditor extends AbstractCellEditor implements TableCellEditor {
private final JPanel p = new JPanel(new GridBagLayout());
private final JComboBox<Integer> cb = new JComboBox<>();
protected ComboCellEditor() {
super();
cb.setEditable(true);
cb.addActionListener(e -> fireEditingStopped());
p.add(cb);
p.setOpaque(false);
}
#Override public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column) {
if (value instanceof ComboBoxModel) {
#SuppressWarnings("unchecked")
ComboBoxModel<Integer> m = (ComboBoxModel<Integer>) value;
cb.setModel(m);
}
return p;
}
#Override public Object getCellEditorValue() {
#SuppressWarnings("unchecked")
DefaultComboBoxModel<Integer> m = (DefaultComboBoxModel<Integer>) cb.getModel();
if (cb.isEditable()) {
Object o = cb.getEditor().getItem();
if (o instanceof Integer && m.getIndexOf((Integer) o) < 0) {
Integer value = (Integer) o;
int n = m.getSize();
Vector<Integer> list = new Vector<>(n + 1);
for (int i = 0; i < n; i++) {
list.add(m.getElementAt(i));
}
list.add(value);
Collections.sort(list);
m = new DefaultComboBoxModel<Integer>(list);
cb.setModel(m);
cb.setSelectedIndex(m.getIndexOf(value));
}
}
return m;
}
}
Override the getCellEditor(...) method to return the combo box:
import java.awt.*;
import java.util.List;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.table.*;
public class TableComboBoxByRow extends JPanel
{
List<String[]> editorData = new ArrayList<String[]>(3);
public TableComboBoxByRow()
{
setLayout( new BorderLayout() );
// Create the editorData to be used for each row
editorData.add( new String[]{ "Red", "Blue", "Green" } );
editorData.add( new String[]{ "Circle", "Square", "Triangle" } );
editorData.add( new String[]{ "Apple", "Orange", "Banana" } );
// Create the table with default data
Object[][] data =
{
{"Color", "Red"},
{"Shape", "Square"},
{"Fruit", "Banana"},
{"Plain", "Text"}
};
String[] columnNames = {"Type","Value"};
DefaultTableModel model = new DefaultTableModel(data, columnNames);
JTable table = new JTable(model)
{
// Determine editor to be used by row
public TableCellEditor getCellEditor(int row, int column)
{
int modelColumn = convertColumnIndexToModel( column );
if (modelColumn == 1 && row < 3)
{
JComboBox<String> comboBox1 = new JComboBox<String>( editorData.get(row));
return new DefaultCellEditor( comboBox1 );
}
else
return super.getCellEditor(row, column);
}
};
JScrollPane scrollPane = new JScrollPane( table );
add( scrollPane );
// table.getColumnModel().getColumn(1).setCellRenderer(new ComboBoxRenderer2() );
}
/*
class ComboBoxRenderer2 extends DefaultTableCellRenderer
{
#Override
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
label.setIcon(UIManager.getIcon("Table.descendingSortIcon"));
return label;
}
}
*/
private static void createAndShowUI()
{
JFrame frame = new JFrame("Table Combo Box by Row");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new TableComboBoxByRow() );
frame.setSize(200, 200);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}

JTable disable Column Selection

I am trying to disable selection of rows in all but 1 column in a JTable. (Layer Column in the example screenshot). In the other columns I have spinners and checkboxes that I want the user to be able to interact with, without effecting the selections in the Layer Column.
My initial attempt was to store up any selected rows as they occur, and then revert to that set of selected rows when a cell outside of column A is selected. It sort of works, but the problem is that it "flashes" when the other cell is selected, before it reverts it back. How can I prevent the "flash"?
Here is an example I set up to illustrate the problem:
public class TableTest {
static int[] selectedRows = new int[0];
final static String[] columns = new String[] { "Layer", "Enabled", "Read Only", "Storage" };
final static DefaultTableModel model = new DefaultTableModel(new Vector(), new Vector(Arrays.asList(columns))) {
#Override
public void setValueAt(Object obj, int row, int col) {
if (obj instanceof Boolean || obj instanceof Integer) {
Object localObject = super.getValueAt(row, col);
if (localObject instanceof Integer) {
Integer val = (Integer) localObject;
((SpinnerCell) obj).getSpinner().setValue(val);
} else if (localObject instanceof Boolean) {
Boolean val = (Boolean) localObject;
((CheckboxCell) obj).getCheckBox().setEnabled(val);
}
} else {
super.setValueAt(obj, row, col);
}
}
#Override
public boolean isCellEditable(int rowIndex, int colIndex) {
return colIndex != 0;
}
};
public static void main(String[] a) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JTable table = new JTable(model) {
#Override
public TableCellRenderer getCellRenderer(final int rowIndex, int colIndex) {
int reaRowlIndex = convertRowIndexToModel(rowIndex);
int realColumnIndex = convertColumnIndexToModel(colIndex);
Object o = model.getValueAt(reaRowlIndex, realColumnIndex);
if (o instanceof TableCellRenderer) {
return (TableCellRenderer) o;
} else {
return super.getCellRenderer(reaRowlIndex, realColumnIndex);
}
}
//
#Override
public TableCellEditor getCellEditor(final int rowIndex, int colIndex) {
int reaRowlIndex = convertRowIndexToModel(rowIndex);
int realColumnIndex = convertColumnIndexToModel(colIndex);
Object o = model.getValueAt(reaRowlIndex, realColumnIndex);
if (o instanceof TableCellEditor) {
return (TableCellEditor) o;
} else {
return super.getCellEditor(reaRowlIndex, realColumnIndex);
}
}
};
table.getTableHeader().setReorderingAllowed(false);
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent arg0) {
if (table.getSelectedColumn() == 0) {
selectedRows = table.getSelectedRows();
System.out.println("Selected Rows before " + Arrays.toString(selectedRows));
}
}
});
final ListSelectionModel columnListSelectionModel = table.getColumnModel().getSelectionModel();
columnListSelectionModel.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
if (table.getSelectedColumn() != 0) {
table.clearSelection();
System.out.println("Selected Rows during " + Arrays.toString(table.getSelectedRows()));
for (int i = 0; i < selectedRows.length; i++) {
table.getSelectionModel().addSelectionInterval(selectedRows[i], selectedRows[i]);
}
System.out.println("Selected Rows after " + Arrays.toString(table.getSelectedRows()));
}
}
});
model.addRow(new Object[] { "Bird", new CheckboxCell(new JCheckBox()),
new CheckboxCell(new JCheckBox()), new SpinnerCell(new JSpinner()) });
model.addRow(new Object[] { "Cat", new CheckboxCell(new JCheckBox()), new CheckboxCell(new JCheckBox()),
new SpinnerCell(new JSpinner()) });
model.addRow(new Object[] { "Dog", new CheckboxCell(new JCheckBox()), new CheckboxCell(new JCheckBox()),
new SpinnerCell(new JSpinner()) });
model.addRow(new Object[] { "Fish", new CheckboxCell(new JCheckBox()),
new CheckboxCell(new JCheckBox()), new SpinnerCell(new JSpinner()) });
model.addRow(new Object[] { "Pig", new CheckboxCell(new JCheckBox()), new CheckboxCell(new JCheckBox()),
new SpinnerCell(new JSpinner()) });
frame.add(new JScrollPane(table));
frame.setSize(300, 200);
frame.setVisible(true);
}
});
}
static class CheckboxCell extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {
private static final long serialVersionUID = 1L;
private JCheckBox checkBox;
public CheckboxCell(JCheckBox inputCheckBox) {
checkBox = inputCheckBox;
}
#Override
public Object getCellEditorValue() {
return checkBox.isSelected();
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
int column) {
return checkBox;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
return checkBox;
}
public JCheckBox getCheckBox() {
return checkBox;
}
#Override
public boolean isCellEditable(EventObject evt) {
return true;
}
public String toString() {
return checkBox.isSelected() + "";
}
}
static class SpinnerCell extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {
private static final long serialVersionUID = 1L;
private JSpinner editSpinner, renderSpinner;
public SpinnerCell() {
editSpinner = new JSpinner();
JTextField tf = ((JSpinner.DefaultEditor) editSpinner.getEditor()).getTextField();
tf.setForeground(Color.black);
renderSpinner = new JSpinner();
JTextField tf2 = ((JSpinner.DefaultEditor) renderSpinner.getEditor()).getTextField();
tf2.setForeground(Color.black);
}
public SpinnerCell(JSpinner showSpinner) {
editSpinner = showSpinner;
JTextField tf = ((JSpinner.DefaultEditor) editSpinner.getEditor()).getTextField();
tf.setForeground(Color.black);
renderSpinner = showSpinner;
JTextField tf2 = ((JSpinner.DefaultEditor) renderSpinner.getEditor()).getTextField();
tf2.setForeground(Color.black);
}
#Override
public Object getCellEditorValue() {
return editSpinner.getValue();
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
int column) {
return editSpinner;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
return renderSpinner;
}
public String toString() {
return editSpinner.getValue().toString();
}
public JSpinner getSpinner() {
return editSpinner;
}
#Override
public boolean isCellEditable(EventObject evt) {
return true;
}
}
}
Here's my short example:
Override JTable#changeSelection(...)
table.setCellSelectionEnabled(true);
Override ListSelectionModel#isSelectedIndex(...)
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class TableTest2 {
public JComponent makeUI() {
String[] columnNames = {"Layer", "Enabled", "Read Only"};
Object[][] data = {
{"Bird", true, false}, {"Cat", true, false},
{"Dog", true, false}, {"Fish", true, false}, {"Pig", true, false}
};
TableModel model = new DefaultTableModel(data, columnNames) {
#Override public Class<?> getColumnClass(int column) {
return getValueAt(0, column).getClass();
}
#Override public boolean isCellEditable(int row, int col) {
return col != 0;
}
};
JTable table = new JTable(model) {
#Override public void changeSelection(
int rowIndex, int columnIndex, boolean toggle, boolean extend) {
if (convertColumnIndexToModel(columnIndex) != 0) {
return;
}
super.changeSelection(rowIndex, columnIndex, toggle, extend);
}
};
table.setAutoCreateRowSorter(true);
table.setCellSelectionEnabled(true);
table.getColumnModel().setSelectionModel(new DefaultListSelectionModel() {
#Override public boolean isSelectedIndex(int index) {
return table.convertColumnIndexToModel(index) == 0;
}
});
return new JScrollPane(table);
}
public static void main(String... args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new TableTest2().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
If you want to prevent the flash you need to intercept an event before the selection is made because table.getSelectedColumn() only works after a selection has been made and you will always see that selection flash.
You could use a key listener to check for "Right Arrow", "Number Pad Right" and "End" key presses and a mouse clicked listener to check where the user is clicking and then change the event to instead select column A only.
For example see this answer showing how to check where a user clicks the mouse before a selection is made: https://stackoverflow.com/a/7351053/1270000
Then you just need to add some code to select the correct cell in column A instead, and don't forget to consume the event "e.consume()" to prevent the original event from completing with the wrong user selection.
Haven't tried it but you might be able to use a custom ListSelectionModel.
Here is an example that lets you toggle the selection of a row on/off:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ToggleListSelectionModel extends DefaultListSelectionModel
{
#Override
public void setSelectionInterval(int index0, int index1)
{
// Select multiple lines
if (index0 != index1)
{
super.addSelectionInterval(index0, index1);
return;
}
// Toggle selection of a single line
if (super.isSelectedIndex(index0))
{
super.removeSelectionInterval(index0, index0);
}
else
{
super.addSelectionInterval(index0, index0);
}
}
private static void createAndShowGUI()
{
String[] numbers = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" };
final JList<String> list = new JList<String>( numbers );
list.setVisibleRowCount( numbers.length );
list.setSelectionModel(new ToggleListSelectionModel());
// list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
JButton clear = new JButton("Clear");
clear.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
list.clearSelection();
}
});
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(list), BorderLayout.CENTER);
frame.add(clear, BorderLayout.PAGE_END);
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
In your case I guess you would need access to the table so you can use the getSelectedColumn() method.
You would remove the logic from the example above and replace it with something like:
if (table.getSelectedColumn() == 0)
super.setSelectionInterval(index0, index1);

Custom TableCellRenderer ignored by Look&Feel

I have a JTable for which I have provided a custom TableCellRenderer that colors numeric cells in red/gray/green depending on their value (<0, 0, >0).
However, when I use the Nimbus L&F, the label.setForeground() method is ignored: when calling label.getForeground() I can see that the number has the right color, for example red, but on the screen it comes black. If I remove the L&F it works fine.
Is there a way to gently ask the L&F to accept using my color for that cell?
ps: I know that the javadoc of setForeground() is clear about the fact that the L&F might ignore the call, so I'm looking for a workaround.
think are complicated by using JLabel,
if you'll use Components then there no needed to override NimbusDefaults or Painter,
sorry I have no ideas to playing with Nimbus & NimbusDefaults & Renderer, because I have another favorite L&F please read some more info about Look and Feels
(without override NimbusDefaults from JCheckBox, this issue are solved a few times on this forum)
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
public class TablePrepareRenderer extends JFrame {
private static final long serialVersionUID = 1L;
private JTable table;
public TablePrepareRenderer() {
Object[] columnNames = {"Type", "Company", "Shares", "Price", "Boolean"};
Object[][] data = {
{"Buy", "IBM", new Integer(1000), new Double(80.50), false},
{"Sell", "MicroSoft", new Integer(2000), new Double(6.25), true},
{"Sell", "Apple", new Integer(3000), new Double(7.35), true},
{"Buy", "Nortel", new Integer(4000), new Double(20.00), false}
};
DefaultTableModel model = new DefaultTableModel(data, columnNames) {
private static final long serialVersionUID = 1L;
#Override
public Class getColumnClass(int column) {
return getValueAt(0, column).getClass();
}
/*#Override
public Class getColumnClass(int column) {
switch (column) {
case 0:
return String.class;
case 1:
return String.class;
case 2:
return Integer.class;
case 3:
return Double.class;
default:
return Boolean.class;
}
}*/
};
table = new JTable(model) {
private static final long serialVersionUID = 1L;
#Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
Component c = super.prepareRenderer(renderer, row, column);
int firstRow = 0;
int lastRow = table.getRowCount() - 1;
if (row == lastRow) {
((JComponent) c).setBackground(Color.red);
} else if (row == firstRow) {
((JComponent) c).setBackground(Color.blue);
} else {
((JComponent) c).setBackground(table.getBackground());
}
return c;
}
};
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane(table);
getContentPane().add(scrollPane);
}
/*private static String[] suffix = new String[]{"", "k", "m", "b", "t"};
private static int MAX_LENGTH = 4;
private static String format(double number) {
String r = new DecimalFormat("##0E0").format(number);
r = r.replaceAll("E[0-9]", suffix[Character.getNumericValue(r.charAt(r.length() - 1)) / 3]);
return r.length() > MAX_LENGTH ? r.replaceAll("\\.[0-9]+", "") : r;
}*/
public static void main(String[] args) {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception fail) {
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TablePrepareRenderer frame = new TablePrepareRenderer();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
/*long[] numbers = new long[]{1000, 5821, 10500, 101800, 2000000, 7800000, 92150000, 123200000, 99999900};
for (long number : numbers) {
System.out.println(number + " = " + format(number));
}*/
}
}
Ok, thanks to mKorbel's answer, I realised that I was using ColorUIResource instead of Color. In other words:
label.setForeground(Color.red); //works
label.setForeground(new ColorUIResource(Color.red)); //doesn't work
I'm not sure I understand why one works and not the other (Color is the direct superclass of ColorUIResource), but problem solved.

JTextArea as cell renderer does not seem to work with wrap style word

I have a JTextArea that I'm using as a cell renderer for a table. In the getTableCellRenderercomponent method I have:
setText(getTextForCell());
setSize(table.getColumnModel().getColumn(column).getWidth(), 0);
getUI().getRootView(textArea).setSize(textArea.getWidth(), 0f);
updateSize();
private void updateSize() {
int prefHeight = textArea.getPreferredSize().height;
int currHeight = table.getRowHeight(r);
if (prefHeight > currHeight) {
table.setRowHeight(row, prefHeight);
}
When the text area uses wrap style word, it is sometimes a row short.
If I call this updateSize method from outside getTableCellRendererComponent then it works properly. But with a large table, calling update size on all rows whenever a column size adjusts is not feasible because it is too slow, so I've been trying to find a way to do the resize during the row rendering.
There is a related Java bug (that is marked as fixed but it does not appear that it really is) http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4446522, but this workaround does not appear to work when using word wrap.
Can anyone provide an alternative on how to make this work properly?
An alternative works properly
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.text.*;
public class AutoWrapInJTable {
public AutoWrapInJTable() {
String[] columnNames = {"TextAreaCellRenderer"};
Object[][] data = {
{"123456789012345678901234567890"},
{"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddx"},
{"----------------------------------------------0"},
{">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>|"},};
TableModel model = new DefaultTableModel(data, columnNames) {
private static final long serialVersionUID = 1L;
#Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
JTable table = new JTable(model) {
private static final long serialVersionUID = 1L;
#Override
public void doLayout() {
TableColumn col = getColumnModel().getColumn(0);
for (int row = 0; row < getRowCount(); row++) {
Component c = prepareRenderer(col.getCellRenderer(), row, 0);
if (c instanceof JTextArea) {
JTextArea a = (JTextArea) c;
int h = getPreferredHeight(a) + getIntercellSpacing().height;
if (getRowHeight(row) != h) {
setRowHeight(row, h);
}
}
}
super.doLayout();
}//http://tips4java.wordpress.com/2008/10/26/text-utilities/
private int getPreferredHeight(JTextComponent c) {
Insets insets = c.getInsets();
View view = c.getUI().getRootView(c).getView(0);
int preferredHeight = (int) view.getPreferredSpan(View.Y_AXIS);
return preferredHeight + insets.top + insets.bottom;
}
};
table.setEnabled(false);
table.setShowGrid(false);
table.setTableHeader(null);
table.getColumnModel().getColumn(0).setCellRenderer(new TextAreaCellRenderer());
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane sp = new JScrollPane(table);
sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
JPanel p = new JPanel(new BorderLayout());
p.add(sp);
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.add(p);
//f.pack();
f.setSize(200, 200);
f.setLocation(150, 150);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
AutoWrapInJTable autoWrapInJTable = new AutoWrapInJTable();
}
});
}
}
class TextAreaCellRenderer extends JTextArea implements TableCellRenderer {
private static final long serialVersionUID = 1L;
private final Color evenColor = new Color(230, 240, 255);
public TextAreaCellRenderer() {
super();
setLineWrap(true);
setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
if (isSelected) {
setForeground(table.getSelectionForeground());
setBackground(table.getSelectionBackground());
} else {
setForeground(table.getForeground());
setBackground(table.getBackground());
setBackground((row % 2 == 0) ? evenColor : getBackground());
}
setFont(table.getFont());
setText((value == null) ? "" : value.toString());
return this;
}
}

Categories