I have a JXTable set up to paint the selected cell in a certain color. However, whenever the selected cell is in the top row, it appears as though ALL cells in the table are painted.
Can anyone help me understand why, and how to resolve this issue?
The smallest working example to demonstrate this issue is below.
Additional info: the DefaultTableCellRenderer is re-applied every time Paint() is called, because it is a placeholder for the CustomTableCellRenderer that I use in my full program. Interestingly, if I remove the DefaultTableCellRenderer line, my issue is resolved. Why is this? Surely, if a renderer is not specified, the Default is used anyway??
import java.awt.Color;
import java.awt.Component;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.decorator.ComponentAdapter;
import org.jdesktop.swingx.decorator.HighlightPredicate;
public class MainClass {
public static void main(String[] args) {
JFrame frame = new JFrame();
CustomTableModel tableModel = new CustomTableModel();
JXXTable table = new JXXTable(tableModel);
JScrollPane scrollPane = new JScrollPane(table);
SelectionListener listener = new SelectionListener(table);
table.getSelectionModel().addListSelectionListener(listener);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(scrollPane);
frame.setVisible(true);
frame.pack();
}
}
class JXXTable extends JXTable {
public JXXTable(CustomTableModel model){ super(model); }
public void Paint(){
this.setDefaultRenderer(Object.class, new DefaultTableCellRenderer());
this.setHighlighters();
HighlightPredicate predicate = new HighlightPredicate() {
public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
return adapter.hasFocus();
}
};
this.addHighlighter(new ColorHighlighter(predicate, null, null, new Color(115,164,209), Color.WHITE));
}
}
class SelectionListener implements ListSelectionListener {
JXXTable table;
public SelectionListener(JXXTable table) { this.table = table; }
public void valueChanged(ListSelectionEvent e) { table.Paint(); }
}
class CustomTableModel extends AbstractTableModel {
ArrayList<Object[]> al;
public CustomTableModel() {
al = new ArrayList<Object[]>();
Object[] row = {1,2,3,"A","Collection","of","Random","Strings",9,10}; al.add(row);
Object[] row2 = {11,12,13,"Another","Collection","Of","Random","Strings",19,20}; al.add(row2);
Object[] row3 = {11,12,13,"Another","Collection","Of","Random","Strings",19,20}; al.add(row3);
Object[] row4 = {11,12,13,"It","just","gets","more","random...",19,20}; al.add(row4);
}
public int getRowCount() { return al.size(); }
public int getColumnCount() { return 10; }
public Object getValueAt(int rowIndex, int columnIndex) { return al.get(rowIndex)[columnIndex]; }
}
I "think" part of the problem has to do with you adding ANOTHER highlighter EVERY time the selection changes.
Instead, consider adding the highlight only when you initialize the table...
Having said that, there's also no need to extend the JXTable, you're not really adding any new functionality to the class. You could, instead, create a factory of some kind that configured that table you want
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.decorator.HighlightPredicate;
public class MainClass {
public static void main(String[] args) {
new MainClass();
}
public MainClass() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
CustomTableModel tableModel = new CustomTableModel();
JXTable table = new JXTable(tableModel);
table.setDefaultRenderer(Object.class, new CustomTableCellRenderer());
HighlightPredicate predicate = new HighlightPredicate() {
#Override
public boolean isHighlighted(Component cmpnt, org.jdesktop.swingx.decorator.ComponentAdapter ca) {
System.out.println(ca.getComponent());
return ca.hasFocus();
}
};
table.addHighlighter(new ColorHighlighter(predicate, null, null, new Color(115, 164, 209), Color.WHITE));
JScrollPane scrollPane = new JScrollPane(table);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(scrollPane);
frame.setVisible(true);
frame.pack();
}
});
}
class CustomTableCellRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
return (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
}
class CustomTableModel extends AbstractTableModel {
ArrayList<Object[]> al;
public CustomTableModel() {
al = new ArrayList<Object[]>();
Object[] row = {1, 2, 3, "A", "Collection", "of", "Random", "Strings", 9, 10};
al.add(row);
Object[] row2 = {11, 12, 13, "Another", "Collection", "Of", "Random", "Strings", 19, 20};
al.add(row2);
Object[] row3 = {11, 12, 13, "Another", "Collection", "Of", "Random", "Strings", 19, 20};
al.add(row3);
Object[] row4 = {11, 12, 13, "It", "just", "gets", "more", "random...", 19, 20};
al.add(row4);
}
public int getRowCount() {
return al.size();
}
public int getColumnCount() {
return 10;
}
public Object getValueAt(int rowIndex, int columnIndex) {
return al.get(rowIndex)[columnIndex];
}
}
}
For others having this problem, this was my solution.
The problem was that a new TableCellRenderer object was being created every time the Paint() method of the table was being called (ie. every time the selection changed). The solution was to create the renderer object as a field of the table object, and simply to re-apply it (rather than re-create it) in the Paint() method. This renderer also needed to be applied during the table's constructor.
Another code improvement I made during this process was to apply the same rule above to the ColorHighlighter and HighlightPredicate objects.
ie. the table class becomes:
class JXXTable extends JXTable {
DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
HighlightPredicate predicate = new HighlightPredicate(){
public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
return adapter.hasFocus();
}
};
Highlighter cellHighlight = new ColorHighlighter(predicate, null, null, new Color(115,164,209), Color.WHITE);
public JXXTable(CustomTableModel model){
super(model);
this.setDefaultRenderer(Object.class, renderer);
}
public void Paint(){
this.setDefaultRenderer(Object.class, renderer);
this.setHighlighters();
this.addHighlighter(cellHighlight);
}
}
Related
I would like to have a JTable with JComboBoxes that is always visible. My problem is when I click on a JComboBox and select a value, my JComboBox is not updated.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
public class trial extends JFrame {
private ArrayList<JComboBox> comboslist= new ArrayList <JComboBox>();/*list of my
comboboxes*/
public static void main(String[] args) {
trial tr= new trial();
}
public trial(){
this.setSize(new Dimension(600,100));
String[] ColData={"Index","Field Name","Value","Range"};
Object[][] TrowData={ };
DefaultTableModel model = new DefaultTableModel(TrowData, ColData);
final List<TableCellEditor> editors = new ArrayList<TableCellEditor>();
JComboBox combo1= new JComboBox();
combo1.addItem("don");
combo1.addItem("mump");
combo1.setEditable(true);
combo1.setBackground(Color.WHITE);
Object[] rowdata = {"cars","books","chicken","soup"};
model.addRow(rowdata);
DefaultCellEditor dce1 = new DefaultCellEditor(combo1);
editors.add(dce1);
JComboBox combo2= new JComboBox();
combo2.addItem("sop");
combo2.addItem("act");
combo2.setEditable(true);
combo2.setBackground(Color.WHITE);
Object[] rowdata2 = {"goats","cats","salads","dogs"};
model.addRow(rowdata2);
DefaultCellEditor dce2= new DefaultCellEditor(combo2);
editors.add(dce2);
comboslist.add(combo1);
comboslist.add(combo2);
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 == 2)
return editors.get(row);
else
return super.getCellEditor(row, column);
}
};
table.setRowHeight(0, 30);
table.setRowHeight(1, 30);
table.setPreferredScrollableViewportSize(new Dimension(800, 150));
table.setFillsViewportHeight(true);
TableColumn valCol= table.getColumnModel().getColumn(2);
int x=0;
for(JComboBox cb: comboslist ){
valCol.setCellRenderer(new ComboBoxCellRenderer(cb,x++));
}
this.add(table);
this.setVisible(true);
}
private class ComboBoxCellRenderer extends JComboBox implements TableCellRenderer {
private JComboBox combo;
private int idx=0;
private ComboBoxCellRenderer comboRenderer;
public ComboBoxCellRenderer(JComboBox com,int ind)
{
for (int i = 0; i < com.getItemCount(); i++)
{
addItem(com.getItemAt(i));
}
comboRenderer=this;
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if(comboslist.get(row).getSelectedItem()!=null)
{
if(isSelected && row==idx)
{
setSelectedItem(comboslist.get(row).getSelectedItem().toString());
comboRenderer.setSelectedItem(comboslist.get(row).getSelectedItem().toString());
}
}else
{
setSelectedItem(value);
}
return this;
}
}
}
You've kinda got in backwards and twisted around. Instead of trying to have a number of combobox editors, you should have one, which is then configured based on the row.
Based on this idea, you could have a single class act as both the renderer and the editor, for example
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractCellEditor;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
public class Trial extends JFrame {
public static void main(String[] args) {
Trial tr = new Trial();
}
public Trial() {
this.setSize(new Dimension(600, 100));
String[] ColData = {"Index", "Field Name", "Value", "Range"};
Object[][] TrowData = {};
DefaultTableModel model = new DefaultTableModel(TrowData, ColData);
final List<TableCellEditor> editors = new ArrayList<TableCellEditor>();
model.addRow(new String[] {"cars", "books", "chicken", "soup"});
model.addRow(new String[] {"goats", "cats", "salads", "dogs"});
DefaultComboBoxModel model1 = new DefaultComboBoxModel();
model1.addElement("don");
model1.addElement("mump");
DefaultComboBoxModel model2 = new DefaultComboBoxModel();
model2.addElement("sop");
model2.addElement("act");
Map<Integer, ComboBoxModel<String>> models = new HashMap<>(2);
models.put(0, model1);
models.put(1, model2);
JTable table = new JTable(model);
table.setRowHeight(0, 30);
table.setRowHeight(1, 30);
table.setPreferredScrollableViewportSize(new Dimension(800, 150));
table.setFillsViewportHeight(true);
ComboBoxCellRendererEditor rendererEditor = new ComboBoxCellRendererEditor(models);
table.getColumnModel().getColumn(1).setCellRenderer(rendererEditor);
table.getColumnModel().getColumn(1).setCellEditor(rendererEditor);
this.add(table);
this.setVisible(true);
}
private static class ComboBoxCellRendererEditor extends AbstractCellEditor implements TableCellRenderer, TableCellEditor {
private JComboBox<String> comboBox;
private Map<Integer, ComboBoxModel<String>> models;
public ComboBoxCellRendererEditor(Map<Integer, ComboBoxModel<String>> models) {
comboBox = new JComboBox<>();
comboBox.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
stopCellEditing();
}
});
this.models = models;
}
protected void configure(Object value, int row) {
ComboBoxModel<String> model = models.get(row);
if (model == null) {
model = new DefaultComboBoxModel<>();
}
comboBox.setModel(model);
comboBox.setSelectedItem(value);
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
configure(value, row);
return comboBox;
}
#Override
public Object getCellEditorValue() {
return comboBox.getSelectedItem();
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
configure(value, row);
return comboBox;
}
}
}
adding this piece of code solved everything.
column.setCellRenderer(new TableCellRenderer() {
JComboBox box = new JComboBox();
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
box.removeAllItems();
box.addItem(value.toString());
return box;
}
});
I have got question about adding JComboBox, to Column in Custom table model which extends ObjectTableModel (Table is OmniJTable). I work on it 2 days and cannot solve this problem.
One thing I solved is displaying JComboBox in Column, but right now I have got problem with selecting anything from it (seems it's not editable, and anything like "setEditable()" not working).
Here is code which one I add jComboBox to my OmniJTable with ObjectTableModel.
class CheckBoxCellRenderer extends JComboBox implements TableCellRenderer {
JComboBox combo;
public CheckBoxCellRenderer(JComboBox comboBox) {
this.combo = new JComboBox();
for (int i=0; i<comboBox.getItemCount(); i++){
combo.addItem(comboBox.getItemAt(i));
}
}
#Override
public Component getTableCellRendererComponent(JTable jtable, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
combo.setSelectedItem(value);
return combo;
}
}
private void addComboBoxToStatusColumn(JTable table)
{
final int statusColumnIndex = bazaTelefonowOmniJTable.getColumnModel().getColumnIndex("Status");
TableColumn tmpColum = bazaTelefonowOmniJTable.getColumnModel().getColumn(statusColumnIndex);
final JComboBox comboBox = new JComboBox();
comboBox.setEditable(true);
comboBox.setEnabled(true);
loadRecordStatusFromDictionary(comboBox);
DefaultCellEditor defaultCellEditor=new DefaultCellEditor(comboBox);
tmpColum.setCellEditor(defaultCellEditor);
tmpColum.setCellRenderer(new CheckBoxCellRenderer(comboBox));
bazaTelefonowOmniJTable.setEditable(true);
//table.repaint();
}
As i said, this one adding jComboBox to Column, but i don't know how to make this one to allow me to choose items in jComboBox.
PS: Sry for my english, it's not my primary language.
The simplest thing is not to add a CellRenderer. In that case, the Table renders it as a Label and when clicked the combo box is shown. Here is an example:
package snippet;
import java.awt.Component;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
public class JTableTest extends JFrame {
public JTableTest() {
super(JTableTest.class.getName());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initComponents();
}
private void initComponents() {
JTable table = new JTable(new Object[][] { { "1", "One" }, { "2", "Two" } }, new Object[] { "Column One", "Status" });
addComboBoxToStatusColumn(table);
add(new JScrollPane(table));
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override public void run() {
new JTableTest().setVisible(true);
}
});
}
private void addComboBoxToStatusColumn(JTable table) {
final int statusColumnIndex = table.getColumnModel().getColumnIndex("Status");
TableColumn tmpColum = table.getColumnModel().getColumn(statusColumnIndex);
final JComboBox comboBox = new JComboBox();
loadRecordStatusFromDictionary(comboBox);
DefaultCellEditor defaultCellEditor = new DefaultCellEditor(comboBox);
tmpColum.setCellEditor(defaultCellEditor);
}
private void loadRecordStatusFromDictionary(JComboBox comboBox) {
comboBox.addItem("Two");
comboBox.addItem("Four");
comboBox.addItem("Six");
}
}
You also need to override the isCellEditable method from your model.
model = DaneTableModel(some arg) {
public boolean isCellEditable(int row, int col) {
if(col == STATUS_COLUMN) return true ;
return false;
}
}
I notice if you set a new model into a JTable, if the number of rows is lower than the highest selected row, this can happen:
java.lang.IndexOutOfBoundsException: Row index 2 is out of bounds (0..1)
at com.acme.MyTableModel.getRowObject(MyTableModel.java:184)
at com.acme.MyCellRenderer.getTableCellRendererComponent(MyCellRenderer.java:144)
at javax.swing.JTable$AccessibleJTable.getAccessibleChild(JTable.java:7039)
at javax.swing.JTable$AccessibleJTable.getAccessibleAt(JTable.java:7426)
at javax.swing.JTable$AccessibleJTable.valueChanged(JTable.java:6939)
at javax.swing.DefaultListSelectionModel.fireValueChanged(DefaultListSelectionModel.java:184)
at javax.swing.DefaultListSelectionModel.fireValueChanged(DefaultListSelectionModel.java:164)
at javax.swing.DefaultListSelectionModel.fireValueChanged(DefaultListSelectionModel.java:211)
at javax.swing.DefaultListSelectionModel.changeSelection(DefaultListSelectionModel.java:405)
at javax.swing.DefaultListSelectionModel.changeSelection(DefaultListSelectionModel.java:415)
at javax.swing.DefaultListSelectionModel.removeSelectionIntervalImpl(DefaultListSelectionModel.java:576)
at javax.swing.DefaultListSelectionModel.clearSelection(DefaultListSelectionModel.java:420)
at javax.swing.JTable.clearSelection(JTable.java:2117)
at javax.swing.JTable.clearSelectionAndLeadAnchor(JTable.java:2125)
at javax.swing.JTable.tableChanged(JTable.java:4370)
at javax.swing.JTable.setModel(JTable.java:3688)
Whose bug is this? Should Swing be checking this index before asking for a renderer, or is it the renderer's job to check the index?
Edit:
Here's a much reduced example, just in case there really is a bug elsewhere. Press the button until row 5 is visible, then select row 5 and press the button again.
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableRowSorter;
public class BreakMyTable implements Runnable {
JTable table;
public static void main(String[] args) {
SwingUtilities.invokeLater(new BreakMyTable());
}
#Override
public void run() {
table = new JTable();
installNewModel();
TableColumnModel columnModel = new DefaultTableColumnModel();
TableColumn column1 = new TableColumn(0, 100);
column1.setCellRenderer(new CustomRenderer(table.getDefaultRenderer(Integer.class)));
columnModel.addColumn(column1);
TableColumn column2 = new TableColumn(1, 400);
columnModel.addColumn(column2);
table.setColumnModel(columnModel);
table.setAutoCreateColumnsFromModel(false);
JToolBar toolBar = new JToolBar();
toolBar.setFloatable(false);
toolBar.add(new AbstractAction("Replace model") {
#Override
public void actionPerformed(ActionEvent e) {
installNewModel();
}
});
JFrame frame = new JFrame();
frame.setLayout(new BorderLayout());
frame.add(toolBar, BorderLayout.PAGE_START);
frame.add(new JScrollPane(table), BorderLayout.CENTER);
frame.setSize(500, 400);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public void installNewModel() {
int rows = table.getModel() == null ? 5 :
table.getModel().getRowCount() == 5 ? 6 : 5;
DefaultTableModel model = new CustomModel(rows, 2);
for (int row = rows - 1; row >= 0; row--) {
model.setValueAt(row, row, 0);
}
table.setModel(model);
table.setRowSorter(new TableRowSorter<>(model));
}
private class CustomModel extends DefaultTableModel {
public CustomModel(int rowCount, int columnCount) {
super(rowCount, columnCount);
}
// In the real implementation, getValueAt() is implemented by calling getRowObject().
public Object getRowObject(int row) {
if (row < 0 || row >= getRowCount()) {
throw new IndexOutOfBoundsException("Out of bounds: " + row);
}
return getValueAt(row, 0);
}
}
private class CustomRenderer implements TableCellRenderer {
private final TableCellRenderer delegateRenderer;
private CustomRenderer(TableCellRenderer delegateRenderer) {
this.delegateRenderer = delegateRenderer;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
int modelIndex = table.convertRowIndexToModel(row);
Object rowObject = ((CustomModel) table.getModel()).getRowObject(modelIndex);
// Would usually use the row object to decorate the cell somehow, e.g. add an icon.
return delegateRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
}
}
I can reproduce it quite reliably and I already know that a solution is to dodge the NPE in the renderer. I just want to know whether the values passed to the renderer are supposed to be checked before I receive them. If they are, then I will consider my solution to be a workaround and will report the bug upstream. If the renderer is supposed to check them, I will consider my solution to be a bugfix.
Probably a noob question, but im new to java. I have a need for a checkbox list which I found is not supported in swing, but I found this custom control here
http://www.devx.com/tips/Tip/5342
So I created a class file named CheckBoxList, and copied the code from the link into it:
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
public class CheckBoxList extends JList
{
protected static Border noFocusBorder =
new EmptyBorder(1, 1, 1, 1);
public CheckBoxList()
{
setCellRenderer(new CellRenderer());
addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
int index = locationToIndex(e.getPoint());
if (index != -1) {
JCheckBox checkbox = (JCheckBox)
getModel().getElementAt(index);
checkbox.setSelected(
!checkbox.isSelected());
repaint();
}
}
}
);
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
protected class CellRenderer implements ListCellRenderer
{
public Component getListCellRendererComponent(
JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus)
{
JCheckBox checkbox = (JCheckBox) value;
checkbox.setBackground(isSelected ?
getSelectionBackground() : getBackground());
checkbox.setForeground(isSelected ?
getSelectionForeground() : getForeground());
checkbox.setEnabled(isEnabled());
checkbox.setFont(getFont());
checkbox.setFocusPainted(false);
checkbox.setBorderPainted(true);
checkbox.setBorder(isSelected ?
UIManager.getBorder(
"List.focusCellHighlightBorder") : noFocusBorder);
return checkbox;
}
}
}
The problem is I don't know how to implement it in my GUI file. I tried a lot of code, but they never showed an example. Just
To use the class, simply instantiate it, then pass it an array of
JCheckBox objects (or subclasses of JCheckBox objects) by calling
setListData
So does that mean that I will not see the control in the Graphical Design view? My client wants to be able to edit it himself and add stuff so I want it to be easy and graphical if possible. If someone could show an example of instantiating it or give a good hint I would appreciate it. Thanks!
Can you just tell me how?
Use a one column JTable and an appropriate renderer and editor. Based on this example, the code below relies on the default renderer for a data value of type Boolean.Class. A more general example is cited here.
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
/** #see https://stackoverflow.com/a/13919878/230513 */
public class CheckTable {
private static final CheckModel model = new CheckModel(5000);
private static final JTable table = new JTable(model) {
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(150, 300);
}
#Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
JCheckBox jcb = (JCheckBox) super.prepareRenderer(renderer, row, column);
jcb.setHorizontalTextPosition(JCheckBox.LEADING);
jcb.setText(String.valueOf(row));
return jcb;
}
};
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame("CheckTable");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(1, 0));
f.add(new JScrollPane(table));
f.add(new DisplayPanel(model));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
private static class DisplayPanel extends JPanel {
private DefaultListModel dlm = new DefaultListModel();
private JList list = new JList(dlm);
public DisplayPanel(final CheckModel model) {
super(new GridLayout());
this.setBorder(BorderFactory.createTitledBorder("Checked"));
this.add(new JScrollPane(list));
model.addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
dlm.removeAllElements();
for (Integer integer : model.checked) {
dlm.addElement(integer);
}
}
});
}
}
private static class CheckModel extends AbstractTableModel {
private final int rows;
private List<Boolean> rowList;
private Set<Integer> checked = new TreeSet<Integer>();
public CheckModel(int rows) {
this.rows = rows;
rowList = new ArrayList<Boolean>(rows);
for (int i = 0; i < rows; i++) {
rowList.add(Boolean.FALSE);
}
}
#Override
public int getRowCount() {
return rows;
}
#Override
public int getColumnCount() {
return 1;
}
#Override
public String getColumnName(int col) {
return "Column " + col;
}
#Override
public Object getValueAt(int row, int col) {
return rowList.get(row);
}
#Override
public void setValueAt(Object aValue, int row, int col) {
boolean b = (Boolean) aValue;
rowList.set(row, b);
if (b) {
checked.add(row);
} else {
checked.remove(row);
}
fireTableRowsUpdated(row, row);
}
#Override
public Class<?> getColumnClass(int col) {
return getValueAt(0, col).getClass();
}
#Override
public boolean isCellEditable(int row, int col) {
return true;
}
}
}
The code is expecting a list of JCheckBox objects - so this works
CheckBoxList cbList = new CheckBoxList(); // the class you have
JCheckBox check1 = new JCheckBox("One");
JCheckBox check2 = new JCheckBox("two");
JCheckBox[] myList = { check1, check2}; list of checkbox object
cbList.setListData(myList); // set the list data for the object
Small Swing program using your class below
util;
import javax.swing.*;
public class HelloWorldSwing {
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("HelloWorldSwing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
CheckBoxList cbList = new CheckBoxList();
JCheckBox check1 = new JCheckBox("One");
JCheckBox check2 = new JCheckBox("two");
JCheckBox[] myList = { check1, check2};
cbList.setListData(myList);
frame.getContentPane().add(cbList);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
I need help at adding a empty row to my JTable. I use a AbstractTableModel for my JTable! I have here a small executable program for you so it is easier to help me.
Main Class - TestClass.java
package testproject;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JTable;
public class TestClass {
String[] header = {"Name", "Quantity"};
ArrayList<Object[]> data = new ArrayList<Object[]>();
public TestClass() {
data.add(new Object[]{"Apple", "234"});
data.add(new Object[]{"Banana", "123"});
data.add(new Object[]{"Cranberry", "346"});
JFrame frame = new JFrame();
JTable table = new JTable(new Model(data, header));
JMenuBar menubar = new JMenuBar();
JMenu editmenu = new JMenu("Edit");
Action addrowaction = new AbstractAction("Add Row") {
private static final long serialVersionUID = 1433684360133156145L;
#Override
public void actionPerformed(ActionEvent e) {
data.add(new Object[9]);
}
};
frame.add(table);
frame.setJMenuBar(menubar);
menubar.add(editmenu);
editmenu.add(addrowaction);
frame.setLocationByPlatform(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.pack();
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestClass();
}
});
}
}
Table Model - Model.java
package testproject;
import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;
public class Model extends AbstractTableModel{
private static final long serialVersionUID = 6889542282067432083L;
private String[] header;
private ArrayList<Object[]> data;
public Model(ArrayList<Object[]> data, String[] header) {
this.header = header;
this.data = data;
}
#Override
public int getColumnCount() {
return header.length;
}
#Override
public String getColumnName(int column) {
return header[column];
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public Object getValueAt(int row, int column) {
return data.get(row)[column];
}
#Override
public void setValueAt(Object value, int row, int column) {
data.get(row)[column] = value;
fireTableCellUpdated(row, column);
}
}
Basically I want to use a Action in the edit menu to add a empty row. Please do not answer with other links! I searched for it but don't understand it or it isnt suitable for my program!
EDIT:
Updated the source as suggested by MadProgrammer! There is only one problem left. How I update the JTable so the new Row is displayed at the moment I have to resize the Window and the Table is updating!
Thank you for help
As #MadProgrammer comments, arrays are conceptually easy to understand but hard to expand & contract. Instead, use a Collection. In this example, MyModel manages a List<Row> that is implemented as an ArrayList<Row>. Each Row is a Plain Old Java Object (POJO) having an attribute for each column. Try adding a method to insert new rows using the constructor as an example, then try adding a column to Row. The example uses a static nested class to avoid clutter.