Add row to JTable with AbstractTableModel - java

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.

Related

JComboBox in Custom table model

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;
}
}

Very unusual cell highlighting in JXTable

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);
}
}

How do you disallow Drag and Drop between two JTables?

I have two JTables, and they are set up so that you can drag and drop the rows within each JTable. The problem is that it lets me drag a row from one JTable to the other JTable, and I am trying to figure out how to stop that. I only want the user to be able to drag and drop a row within that same JTable.
In other words, when I drag a row outside the current table and hover the mouse over the empty panel space, the mouse cursor displays a circle with a diagonal line through it, which is what I want. However, when I drag the mouse over the other table, it displays the small rectangular "drop" icon, which is what I am trying to block. When the user tries to drag this row on top of other table, I would like the small circle with diagonal line to appear.
This is a working example that demonstrates the problem:
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DragSource;
import java.util.ArrayList;
import java.util.List;
import javax.activation.ActivationDataFlavor;
import javax.activation.DataHandler;
import javax.swing.DropMode;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.TransferHandler;
import javax.swing.table.AbstractTableModel;
public class JTableDnD extends JFrame
{
private JTable tableA;
private JTable tableB;
public JTableDnD()
{
// *** Create First Table ***
List<Object[]> dataA = new ArrayList<Object[]>();
dataA.add(new Object[] {"A1", "A1"});
dataA.add(new Object[] {"A2", "A2"});
dataA.add(new Object[] {"A3", "A3"});
List<String> columnsA = new ArrayList<String>();
columnsA.add("Column 1");
columnsA.add("Column 2");
tableA = new JTable(new TableModel(columnsA, dataA));
tableA.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
tableA.setDragEnabled(true);
tableA.setDropMode(DropMode.INSERT_ROWS);
tableA.setFillsViewportHeight(true);
tableA.setTransferHandler(new TableTransferHandler(tableA));
JScrollPane scrollPaneA = new JScrollPane(tableA);
// *** Create Second Table ***
List<Object[]> dataB = new ArrayList<Object[]>();
dataB.add(new Object[] {"B1", "B1"});
dataB.add(new Object[] {"B2", "B2"});
dataB.add(new Object[] {"B3", "B3"});
List<String> columnsB = new ArrayList<String>();
columnsB.add("Column 1");
columnsB.add("Column 2");
tableB = new JTable(new TableModel(columnsB, dataB));
tableB.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
tableB.setDragEnabled(true);
tableB.setDropMode(DropMode.INSERT_ROWS);
tableB.setFillsViewportHeight(true);
tableB.setTransferHandler(new TableTransferHandler(tableB));
JScrollPane scrollPaneB = new JScrollPane(tableB);
// *** Add ScrollPanes to Panel ***
this.getContentPane().setLayout(new FlowLayout());
add(scrollPaneA);
JPanel emptyPanel = new JPanel();
emptyPanel.setPreferredSize(new Dimension(100, 200));
add(emptyPanel);
add(scrollPaneB);
} // end JTableDnD constructor
private static void createAndShowGUI()
{
JFrame frame = new JTableDnD();
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args)
{
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
interface Reorderable
{
public void reorder(int from, int to);
}
class TableModel extends AbstractTableModel implements Reorderable
{
private List<String> columnNames;
private List<Object[]> data;
public TableModel(List<String> columnNames, List<Object[]> data)
{
super();
this.columnNames = columnNames;
this.data = data;
}
#Override
public void reorder(int from, int to)
{
if (from < to)
{
to--;
}
Object[] row = data.remove(from);
data.add(to, row);
fireTableDataChanged();
}
#Override
public int getRowCount()
{
return data.size();
}
#Override
public int getColumnCount()
{
return columnNames.size();
}
#Override
public String getColumnName(int column)
{
return columnNames.get(column);
}
#Override
public Object getValueAt(int rowIndex, int columnIndex)
{
return data.get(rowIndex)[columnIndex];
}
} // end TableModel
class TableTransferHandler extends TransferHandler
{
private final DataFlavor localObjectFlavor = new ActivationDataFlavor(Integer.class, DataFlavor.javaJVMLocalObjectMimeType, "Integer Row Index");
private JTable table = null;
public TableTransferHandler(JTable table)
{
this.table = table;
}
#Override
protected Transferable createTransferable(JComponent component)
{
return new DataHandler(new Integer(table.getSelectedRow()), localObjectFlavor.getMimeType());
}
#Override
public boolean canImport(TransferHandler.TransferSupport support)
{
boolean b = support.getComponent() == table &&
support.isDrop() &&
support.isDataFlavorSupported(localObjectFlavor);
table.setCursor(b ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop);
return b;
}
#Override
public int getSourceActions(JComponent component)
{
return TransferHandler.COPY_OR_MOVE;
}
#Override
public boolean importData(TransferHandler.TransferSupport support)
{
JTable target = (JTable) support.getComponent();
JTable.DropLocation dropLocation = (JTable.DropLocation) support.getDropLocation();
int index = dropLocation.getRow();
int max = table.getModel().getRowCount();
if (index < 0 || index > max)
{
index = max;
}
target.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
try
{
Integer rowFrom = (Integer) support.getTransferable().getTransferData(localObjectFlavor);
if (rowFrom != -1 && rowFrom != index)
{
((Reorderable) table.getModel()).reorder(rowFrom, index);
if (index > rowFrom)
{
index--;
}
target.getSelectionModel().addSelectionInterval(index, index);
return true;
}
}
catch (Exception e)
{
e.printStackTrace();
}
return false;
}
#Override
protected void exportDone(JComponent component, Transferable transferable, int action)
{
if (action == TransferHandler.MOVE)
{
table.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
} // end TableTransferHandler
I believe I need to add some extra logic to the canImport() method to make sure that the row that is being dropped is from that same table, but I can't seem to figure it out. I've tried inspecting the data inside the TransferSupport object that gets passed into canImport(), but it does not seem to have any function that returns the exact JTable object source.
In the simplified Swing DnD, there is no support for getting at the source of the transferable. The component returned by TransferSupport` is the target of the drop, that is the component that somehow should handle the associated transferable. So if you have a per-component TransferHandler configured with a particular table, the support's component will always be that same table instance and your check will be true trivially.
If you want to enable/disable dropping based on the sender, you'll have to provide it on a per-drag basis: a drag starts in exportAsDrag and ends in exportDone, so you can set/null the sender in those.
#Override
protected void exportDone(JComponent component,
Transferable transferable, int action) {
table = null;
}
#Override
public void exportAsDrag(JComponent comp, InputEvent e, int action) {
table = (JTable) comp;
super.exportAsDrag(comp, e, action);
}
Now you can re-use the same instance of the handler across several instances of table:
TableTransferHandler handler = new TableTransferHandler();
tableA.setTransferHandler(handler);
tableB.setTransferHandler(handler);
As an aside: I wouldn't fiddle with the cursors, they are supporsed to be under complete control of the dnd subsystem.

How to implement checkbox list java

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();
}
});
}
}

Get Selected Rows in JTable using AbstractTableModel

I have a JTable using AbstractTableModel where I have a JCheckBox in the first column for selecting rows. Now, I need to get the selected rows from the table which are checked. Right now, I am sequentially traversing from first row to the last row and getting all the rows that are selected like the following,
List<Integer> selectedRows = new ArrayList<Integer>();
for(int i = 0; i < table.getRowCount(); i++) {
if((Boolean) table.getValuAt(i, 0)) {
selectedRows.add(i);
}
}
The problem here is, I need to traverse all the rows when ever I need to get the selected rows. Right now I am having 10 to 20 rows. But in future I will get around 5000 rows. My question is, if there are 5000 rows and if the user selects only 5000nd (last record) row then I need to traverse all the 5000 rows to get the selected row. Which I think is not a good approach.
One approach which I want to implement is, to add a listener to the JCheckBox column, such that when ever there is a change (SELECTED/DESELECTED) then I need to update my array of the selected rows in the listener class. In this listener class when ever user selectes a JCheckBox I need to call table.getSelectedRow(..) and I need to store if that JCheckBox is selected.
Are there any better approaches ?
In the example below, the TableModel updates a Set<Integer> checked in the implementation of setValueAt(). The model of an adjacent JList listens to the table's model and displays the currently selected row numbers. The example assumes that the number of selected rows is small compared to the number of rows. Note the use of TreeSet, whose iterator retains the natural order of the elements.
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.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;
/** #see http://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);
}
};
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 2;
}
#Override
public String getColumnName(int col) {
return "Column " + col;
}
#Override
public Object getValueAt(int row, int col) {
if (col == 0) {
return row;
} else {
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 col == 1;
}
}
}
I agree with kleopatra. When you create a subclass of the AbstractTableModel, you'll override the setValue( Object value, int rowIndex, int colIndex ). In your overridden method, you just check if the column is the one with your check box, and if so, update the internal data structure appropriately. You can also add a method getCheckedRows() that returns a List< Integer > with the rows in which the check boxes have been selected.

Categories