In a previous question (Convert modelRowIndex to viewRowIndex for sorted JTable) I indicated that I was trying to create a "simple" JTable that used a TableModel to tie an ArrayList to a JTable using a TableModel. My goal was - and still is - to retain all of java's built-in JTable functionality that allows cell editing, row sorting, and column rearranging. Thanks to your help, that functionality now works.
I'm now trying to add the ability to Insert and Delete table rows. The (updated) example I provide here works ... EXCEPT ... under a certain sequence of operations, an "IndexOutOfBoundsException" is thrown. Here's my code:
package tableexample;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
public final class TableExample extends JFrame {
List<REItem> REList;
JTable tblREList;
JButton btnAddInsertRE, btnDeleteRE;
JScrollPane spMain;
JFrame frame;
Container pane;
public TableExample() {
// create and populate the ArrayList
REList = new ArrayList<>();
REList.add(new REItem("Template1", "Comment1"));
REList.add(new REItem("Template2", "Comment2"));
RETableModel retm = new RETableModel(REList); // Connect the List to the TableModel
// create GUI components
frame = new JFrame ("Table Example");
btnAddInsertRE = new JButton("Add/Insert");
btnDeleteRE = new JButton("Delete");
tblREList = new JTable(retm);
tblREList.setAutoCreateRowSorter(true);
spMain = new JScrollPane(tblREList);
// add button ActionListeners
btnAddInsertRE.addActionListener((ActionEvent evt) -> { btnAddInsertREActionPerformed(evt); });
btnDeleteRE.addActionListener((ActionEvent evt) -> { btnDeleteREActionPerformed(evt); });
// place GUI components and make the GUI visible
pane = frame.getContentPane();
pane.setLayout (null);
pane.add(btnAddInsertRE);
pane.add(btnDeleteRE);
pane.add(spMain);
btnAddInsertRE.setBounds (10, 10, 100, 25);
btnDeleteRE.setBounds (120, 10, 100, 25);
spMain.setBounds (10, 45, spMain.getPreferredSize().width, spMain.getPreferredSize().height);
frame.setSize(spMain.getWidth() + 40, spMain.getHeight() + 95);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
} // end TableExample constructor
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
TableExample notUsed = new TableExample();
});
} //end main
private void btnAddInsertREActionPerformed(ActionEvent evt) {
// Add a FileSelection object to the ArrayList
int r = tblREList.getSelectedRow(); // get row selection, if any
if (r < 0) { // no row selected
REList.add(new REItem("NewTemplate", "NewComment")); // append new item to end
r = REList.size()-1; // get index to new item
} else { // else no row selected
REList.add(r, new REItem("NewTemplate", "NewComment")); // insert above selected row
} // row selected or not
spMain.setViewportView(tblREList); // repaint the updated table
tblREList.getSelectionModel().setSelectionInterval(r, r); // select the new row
}
private void btnDeleteREActionPerformed(ActionEvent evt) {
int[] selRows = tblREList.getSelectedRows(); // see if any rows are selected
if (selRows.length>0) { // at least one row is selected
for (int r=selRows.length-1; r>=0; r--) { // delete each row, from the bottom up,
REList.remove(r); // so that indexes are correct and
} // don't change with each delete
tblREList.clearSelection(); // clear the row selection data
spMain.setViewportView(tblREList); // repaint the updated table
} else { // else no row(s) selected
JOptionPane.showMessageDialog(null, "Must select at least one item to delete");
} // no row selected
} // end btnDeleteREActionPerformed
public final class REItem {
String template;
String comment;
public REItem(String tmp, String cmt) {
this.template = tmp;
this.comment = cmt;
}
} // end class REItem
public class RETableModel extends AbstractTableModel {
private List<REItem> reList = new ArrayList();
private final String[] columnNames = { "Template", "Comment" };
public RETableModel(List<REItem> list){
this.reList = list;
}
#Override
public String getColumnName(int column){
return columnNames[column];
}
#Override
public int getRowCount() {
return reList.size();
}
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public Object getValueAt(int row, int column) {
switch (column) {
case 0: return reList.get(row).template;
case 1: return reList.get(row).comment;
}
return null; // default case
}
#Override
public boolean isCellEditable(int row, int column) {
return true;
}
#Override
public Class<?> getColumnClass(int column){
switch (column){
case 0: return String.class;
case 1: return String.class;
}
return null; // default case
}
#Override
public void setValueAt(Object value, int row, int column) {
switch (column) {
case 0: reList.get(row).template = value.toString(); break;
case 1: reList.get(row).comment = value.toString(); break;
}
// uncommenting the below often causes IndexOutOfBoundsException: Invalid range exception
fireTableCellUpdated(row, column);
} // end setValueAt
} // end RETableModel
} // end class TableExample
The problem can be reproduced as follows: Run the above example, click the "Add/Insert" button to append a new row to the table, click either column header to re-sort the table, then click the "Delete" button: An "IndexOutOfBoundsException" is thrown, indicating that the "row" index specified by the TableModel's getValueAt method is flawed.
I assume the issue is related to the need for my TableModel.getValueAt (and maybe .setValueAt ???) method(s) to convert between TableModel column indexes and View column indexes but, for the life of me, I can't figure out how or where to make the conversion. Morevover, this question (Convert modelRowIndex to viewRowIndex for sorted JTable) indicates that a conversion between TableModel and View row-indexes is needed, AND that table re-sorting MUST occur before the index conversion is done.
Try as I might, I cannot figure out how to make the conversion and/or how to make sure that conversion happens AFTER the table is updated and re-sorted. Do I need a Listener? If so, what should it look like?
Can you provide some clarification and help?
First of all, variable names should NOT start with an upper case character. This is a Java convention and it messes with the formatting of the code you post making your code hard to read. Fix your variables and follow Java conventions.
The ArrayList should only be used to initially add data to the model.
After that updates should be done to the TableModel, not the ArrayList. So you need to add methods to your TableModel like addREItem(...) and removeREItem(...).
See Row Table Model for a step-by-step example of how to build a custom TableModel for a given object, including how the code the add???(...) and remove???(...) method.
And if you want to remove selected rows from the table then check out: How to delete multiple rows from JTable , database at a time for a working examples that shows how this can be done using the remove???(...) method.
Related
So I am creating a project which has three columns; one is a check box column the second one is string (words form a neo4j database) and the third for progress bars.
All the columns are displayed and work fine but the progress bar column is invisible as it seems. Here is some code:
//CONSTRUCTOR
public BiogramTableJSedit2Jan9()
{
//*************************************************
//* SETTING UP THE FORM *
//*************************************************
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(200,200,800,300);
setTitle("Netword Data Table");
getContentPane().setLayout(null);
//*************************************************
//********************************************
//CREATING TABLE BLOCK *
//********************************************
//ADD SCROLLPANE
JScrollPane scroll=new JScrollPane();
scroll.setBounds(70,80,600,200);
getContentPane().add(scroll);
//THE TABLE
final JTable table=new JTable();
scroll.setViewportView(table);
//THE MODEL OF THE TABLE
DefaultTableModel model=new DefaultTableModel()
{
//****************************************
//* SETTING TABLE COLUMNS BLOCK *
//****************************************
public Class<?> getColumnClass(int column)
{
switch(column)
{
case 0: // |This is the first column
return Boolean.class; // |First column is set to Boolean as it will contain check boxes
case 1: // |This is the second column
return String.class; // |Second column set to String as it will contain strings
case 2:
return JProgressBar.class; // |This is for the progress bar column (IN PROGRESS - NOT DISPLAYED YET...)
default:
return String.class; // |The table is set to String as default
}
}
};
//Create and run the query in the table
neoQuery= Q1();
resultVariable = session.run(neoQuery.get());
//ASSIGN THE MODEL TO TABLE
table.setModel(model);
model.addColumn("Select"); // |Column for check boxes
model.addColumn("Bigrams"); // |Column for Bigrams
table.getColumn("Status").setCellRenderer(new ProgressCellRender());
//**********************************************
ProgressWorker worker = new ProgressWorker(model); //HAVE AN ERROR HERE
worker.execute();
This is the progress renderer class:
//a table cell renderer that displays a JProgressBar
public class ProgressCellRender extends JProgressBar implements TableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
int progress = 0;
if (value instanceof Float) {
progress = Math.round(((Float) value) * 100f);
} else if (value instanceof Integer) {
progress = (int) value;
}
setValue(progress);
return this;
}
}
Now you can see that I am using getColumn in order to display this column but when I run it you can see the first and second but not the progress bar column. I want it to look similar to this:
I also has a progress worker class that is not fully implemented.
private static class ProgressWorker extends SwingWorker<Void, Integer>
{ //Swing worker class for updating the progress bar
private BiogramTableJSedit2Jan9 model;
private final JProgressBar progress; //declaration for progress bar
public ProgressWorker(JProgressBar model)
{
this.progress = model;
}
#Override
protected Void doInBackground() throws Exception {
return null;
}
I would appreciate if anyone can explain to me why the thirst column is not being displayed. Thanks for any replies in advance.
Because your posted code was not self-contained I set up a minimal test together
with your already posted ProgressCellRender class.
The important bugfix is in method getColumnClass of the DefaultTableModel.
It needs to return Integer.class or Float.class because only those can be
handled by your ProgressCellRender.
And of course, the actual data in that column need to be Integer or Float.
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(Main::initGUI);
}
private static void initGUI() {
JFrame frame = new JFrame("TableModel JProgressBar Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane scroll = new JScrollPane();
frame.getContentPane().add(scroll);
final JTable table = new JTable();
scroll.setViewportView(table);
DefaultTableModel model = new DefaultTableModel() {
public Class<?> getColumnClass(int column) {
switch (column) {
case 0:
return Boolean.class;
case 1:
return String.class;
case 2:
return Integer.class; // !!!!
default:
return String.class;
}
}
};
table.setModel(model);
model.addColumn("Active");
model.addColumn("Name");
model.addColumn("Progress");
table.getColumn("Progress").setCellRenderer(new ProgressCellRender());
model.addRow(new Object[] { true, "aaaa", 14 });
model.addRow(new Object[] { false, "bbbbbbbb", 0 });
model.addRow(new Object[] { true, "ccccc", 2 });
frame.pack();
frame.setVisible(true);
}
}
Then the JProgressBars are rendered correctly in the table:
In your ProgessCellRender you may want to add setStringPainted(true); to get the percentage also rendered as text.
I want show customer details from a MySQL database in a JTable, but I don't see any result in my panel when I click the "show" button.
This is the method to add the table to the JScrollPane:
void addTable()
{
for(int i=0 ; i<myTableModel.getColumnCount();i++)
{
myTableModel.getColumnName(i);
}
showCustomers();
table.setModel(myTableModel);//mytablemodel is a object from MyTableModel Class
scrollPane.add(table); //was on another part of program but i edit it for helping to answers
panel_show.add(scrollPane);
}
And here is MyTableClass, implementing TableModel, with a new method, addCustomer. addCustomer will add a Customer to the CustomerList. I am trying ArrayList for first time to show Customer data but it doesn't work.
Also, columnName will make the table headers with the column names from the database.
public class MyTableModel implements TableModel
{
private ArrayList<Customer> customerList;
private String[] columnName =
{"id", "name", "family", "idc", "age", "sex", "balance", "tel", "haveFamily", "population"};
#Override
public int getRowCount()
{
//return customerList.size();
return 1;
}
#Override
public int getColumnCount()
{
return 10;
}
#Override
public String getColumnName(int columnIndex)
{
return columnName[columnIndex];
}
#Override
public Class<?> getColumnClass(int columnIndex)
{
if(columnIndex == 0)
return Integer.class;
return String.class;
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return false;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex)
{
if(columnIndex == 0)
return customerList.get(rowIndex).getId();
else if(columnIndex == 1)
return customerList.get(rowIndex).getName();
else if(columnIndex == 2)
return customerList.get(rowIndex).getFamily();
else if(columnIndex == 3)
return customerList.get(rowIndex).getIdc();
else if(columnIndex == 4)
return customerList.get(rowIndex).getDate();
else if(columnIndex == 5)
return customerList.get(rowIndex).getSex();
else if(columnIndex == 6)
return customerList.get(rowIndex).getBalance();
else if(columnIndex == 7)
return customerList.get(rowIndex).getTel();
else if(columnIndex == 8)
return customerList.get(rowIndex).isHaveFamily();
else if(columnIndex == 9)
return customerList.get(rowIndex).getPopulation();
else
return null;
}
public void addCustomer(Customer customer)
{
CustomerManager customerManager = new CustomerManager();
customerManager.addCustomer(customer);
customerList.add(customer);
}
The showCustomer method will be open a database connection and create a TableModel with customers, which is then used for the table on my panel.
public void showCustomers()
{
Connection conn = null;
try
{
//String user = "root";
// String pass = null;
//String url = "jdbc:mysql://localhost/estate";
Class.forName("com.mysql.jdbc.Driver").newInstance();
conn = DriverManager.getConnection("jdbc:mysql://localhost/estate", "root", null);
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("SELECT * from customer");
//int columnCount = rsmd.getColumnCount();
while (rs.next())
{
myTableModel.addCustomer(new Customer(1,"david","schmidt","0025674433","31","Male","200000","4545552132","true","2");
}//Original Form --> Customer({"id","name","family","idc","age",
// "sex","balance","tel","haveFamily","population"});
}
In MyTableModel I make an addCustomer method and fill the customer data using a ResultSet. Is it needed?
My question: How can I show my Customers using a JTable?
After some fiddling I now also have a problem with myTableModel.addCustomer(rs.getInt(), ...). The error I am getting is this:
Error in addCustomer,Duplicate entry '0' for KEY PRIMARY
However, I don't have a customer with an ID of 0.
When displaying a JTable you first add the table to a scroll pane and then you add the scroll pane to a panel.
When adding a component to a visible GUI you need to make sure the layout manager has been invoked so that the component has a size and location.
So the basic code would be:
table.setModel(...);
JScrollPane scrollPane = new JScrollPane( table );
panel.add( scrollPane );
panel.revalidate();
panel.repaint();
The other approach is the create an empty table and add the scroll pane to the GUI when you first create the GUI. Then all the code you need is:
table.setModel(...);
Edit:
You question is about displaying a table in a panel, when you click a button. So that is ALL the SSCCE should do. Where the data comes from is irrelevant, so posting code dealing with a database is completely unnecessary, because we can't execute the code. A custom TableModel is irrelevant to the real question.
Here is a simple SSCCE that uses my second suggestion of updating an existing table with a new model. Every time you click the button the number of columns changes. This simulates getting new data from somewhere.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
public class RefreshSSCCE extends JPanel
{
private JTable table = new JTable();
private int columns = 3;
public RefreshSSCCE()
{
setLayout( new BorderLayout() );
JButton refresh = new JButton( "Refresh Data" );
add(refresh, BorderLayout.NORTH);
refresh.addActionListener( new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
refreshData();
}
});
add(new JScrollPane(table), BorderLayout.CENTER);
}
private void refreshData()
{
DefaultTableModel model = new DefaultTableModel(5, columns++);
table.setModel( model );
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Refresh SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new RefreshSSCCE() );
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
See how easy that code is to understand? The code is complete and in one class. It can easily be copied and pasted so others can test the code.
When you simplify the problem then solution is usually much easier. That is why you take the time to create a SSCCE. Even if the SSCCE doesn't work the way you want, we only have a couple of lines of code to look at to understand what you are attempting to do. There is no need to complicate the question with SQL code.
Once you get the simple code working you then modify the refreshData method to get real data. That is what I mean by hardcoding data. There is no need for a dynamic query of the database to demonstrate your problem of displaying a table in a panel.
I am having trouble creating a JTable with scrollbars.
I want a JTable with 2 columns and no visible scrollbars.
If I enlarge one of the columns the scrollbars should become visible and the columns resize.
I followed this answer How to make JTable both AutoResize and horizontall scrollable? and works fine which basically comes down to:
JTable table = new JTable() {
#Override
public boolean getScrollableTracksViewportWidth() {
return getPreferredSize().width < getParent().getWidth();
}
};
table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
However, with this solution I cannot shrink the first column. Only if I enlarge the 2nd column and the scrollbars become visible I can shrink the first one.
The required behavior is that the 2 columns are automatically resizable. Meaning that the 1 column can shrink and afterwards extend without the scrollbars popping up. Only when extending one of the columns, so that the view should extend, the scrollbars should pop up.
A scenario:
Shrink the 1st column -> 2nd one enlarges, no scrollbars
Enlarge the 1st column -> 2nd one shrinks, still no scrollbars
Enlarge the 2nd column -> 1 column stays the same, 2nd one enlarges and scrollbars appear
Any ideas on fixing this?
An SSCCE:
import javax.swing.JDialog;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.WindowConstants;
import javax.swing.table.AbstractTableModel;
import java.awt.BorderLayout;
import java.awt.Container;
public class TableTest {
public TableTest() {
JDialog mainDialog = new JDialog();
mainDialog.setResizable( true );
mainDialog.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE );
Container contentPane = mainDialog.getContentPane();
JTable myTable = new JTable() {
#Override
public boolean getScrollableTracksViewportWidth() {
return getPreferredSize().width < getParent().getWidth();
}
};
myTable.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
myTable.setModel( new MyTableModel() );
JScrollPane scrollPane = new JScrollPane( myTable );
contentPane.add( scrollPane, BorderLayout.CENTER );
mainDialog.pack();
mainDialog.setVisible( true );
}
public static void main( String[] args ) {
new TableTest();
}
private class MyTableModel extends AbstractTableModel {
#Override public int getRowCount() {
return 1;
}
#Override public int getColumnCount() {
return 2;
}
#Override public Object getValueAt( int rowIndex, int columnIndex ) {
return "ARandomValue";
}
}
}
It's not quite enough to override the getTracks method, you have to fool super's layout into doing the right-thingy if tracking:
JTable myTable = new JTable(10, 4) {
private boolean inLayout;
#Override
public boolean getScrollableTracksViewportWidth() {
return hasExcessWidth();
}
#Override
public void doLayout() {
if (hasExcessWidth()) {
// fool super
autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS;
}
inLayout = true;
super.doLayout();
inLayout = false;
autoResizeMode = AUTO_RESIZE_OFF;
}
protected boolean hasExcessWidth() {
return getPreferredSize().width < getParent().getWidth();
}
#Override
public void columnMarginChanged(ChangeEvent e) {
if (isEditing()) {
// JW: darn - cleanup to terminate editing ...
removeEditor();
}
TableColumn resizingColumn = getTableHeader().getResizingColumn();
// Need to do this here, before the parent's
// layout manager calls getPreferredSize().
if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF
&& !inLayout) {
resizingColumn.setPreferredWidth(resizingColumn.getWidth());
}
resizeAndRepaint();
}
};
Might not be entirely complete (probably still isn't, even after the edit to take care of columnMarginChanged, copied from JXTable (of the SwingX project) which support that behaviour by an additional layout property
xTable.setHorizontalScrollEnabled(true);
With the implementation of #kleopatra, I noticed that you get a scrollbar, when you reduce the size of a column and then increase it again just slightly (which happens quite often by accident). So I've slightly changed the code slightly:
protected boolean hasExcessWidth() {
return getPreferredSize().width - getParent().getWidth() < 50;
}
This allows to slowly increase the size of a column without loosing the auto resize.
Not really sure yet if the magic "50" is a good measurement, but works quite well in initial tests
I have an application which uses JTables to display data, and the cells are editable so that the user can change the data. The user can also revert the changes, or load data from an external source. However, if the user reverts/loads the data using a keyboard shortcut, so that mouse focus is not taken away from the table, the currently selected cell does not get reverted. In fact, after the refresh, the cell goes into edit mode! Then when the user navigates away from this cell, a change event is triggered, so the old value gets committed back to the data store.
I have a short example program that demonstrates this problem. It shows a table in which every cell displays the value 0. There is also a File menu with a single menu item called "Increment", which has a keyboard shortcut of Ctrl-I. Each time the Increment command is invoked, it increments the number displayed in all of the cells. To see the problem, do the following:
Compile and run the program
Hit Ctrl-I a bunch of times to invoke the Increment command. Notice that the cell values increment each time.
Click a cell.
Hit Ctrl-I a bunch of times to invoke the Increment command. Notice that all cell values increment except the one that is selected.
I have tried various methods to remove the selection from the table before refreshing it, to no avail. Neither
table.editCellAt(-1, -1);
nor
table.getSelectionModel().clearSelection();
worked, for example.
Here is the sample program:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
public class TableBug extends JFrame {
private static final int ROW_COUNT = 3;
private static final int COL_COUNT = 3;
private int mDataValue = 0;
private DefaultTableModel mTableModel;
// Constructor
public TableBug() {
setTitle("TableBug");
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
// Create table model and table
mTableModel = new DefaultTableModel();
for (int col = 0; col < COL_COUNT; col++) {
mTableModel.addColumn("Value");
}
JTable table = new JTable(mTableModel);
setUpTable(table);
refresh();
// Create menu bar
int keyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
JMenu fileMenu = new JMenu("File");
JMenuItem incrementMenuItem = new JMenuItem("Increment");
incrementMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, keyMask));
incrementMenuItem.addActionListener(new AbstractAction() {
public void actionPerformed(ActionEvent e) {
doIncrement();
}
});
fileMenu.add(incrementMenuItem);
JMenuBar mainMenuBar = new JMenuBar();
mainMenuBar.add(fileMenu);
// Populate GUI
setJMenuBar(mainMenuBar);
add(new JScrollPane(table), BorderLayout.CENTER);
// Display window
pack();
setVisible(true);
}
// Configures the table
private void setUpTable(JTable table) {
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.getTableHeader().setReorderingAllowed(false);
table.getTableHeader().setResizingAllowed(false);
table.setRowSelectionAllowed(false);
}
// Populates the table
private void refresh() {
mTableModel.setRowCount(ROW_COUNT);
for (int col = 0; col < COL_COUNT; col++) {
for (int row = 0; row < ROW_COUNT; row++) {
mTableModel.setValueAt(mDataValue, row, col);
}
}
}
// Handles the Increment menu item
public void doIncrement() {
mDataValue++;
refresh();
}
// Main program
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() {
new TableBug();
}
});
}
}
In your refresh function, check if the table is being edited. If it is, get the row and column that are being edited and stop the cell editing.
private void refresh() {
if (table.isEditing()) {
int row = table.getEditingRow();
int column = table.getEditingColumn();
table.getCellEditor(row, column).stopCellEditing();
}
...
To do this, you'll need to make your table variable accessible (make it a class variable).
How can I put a JCheckbox or a JButton on a specific row and column of a JTable?
Not sure about a button, but here is a working example to put a checkbox:
import javax.swing.*;
import javax.swing.table.*;
public class Test {
public static void main(String [] args) throws Exception {
DefaultTableModel model = new DefaultTableModel(null, new String [] {"CheckMe", "Value"}) {
public Class getColumnClass(int c) {
switch (c) {
case 0: return Boolean.class;
default: return String.class;
}
} };
JTable table = new JTable(model);
JFrame frame = new JFrame("CheckBox Test");
frame.add(table);
model.addRow(new Object [] {true, "This is true"});
model.addRow(new Object [] {false, "This is false"});
frame.pack(); frame.validate();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
As you can tell from khachik's answer support for a check box is provided by a table based on the column class of the column.
However, if you only want a check box on a specific row of a specific column then you need to override the getCellRenderer(...) and getCellEditor(...) methods to return the renderer/editor for the given cell. Something like:
public TableCellEditor getCellEditor(int row, int column)
{
int modelColumn = convertColumnIndexToModel( column );
if (modelColumn == 1 && row < 3)
return getDefaultEditor(Boolean.class);
else
return super.getCellEditor(row, column);
}
For that, you'll have to write a TableCellRenderer and a TableCellEditor.
You can derive from default swing implementations to make it easier.
In each class, you'll have to override the one method of these interfaces, and in it, check the passed row and column arguments; if both row and column match your criteria, then return a JCheckBox or a JButton, otherwise return the JComponent returned by the super implementation (when using default swing implementations of these interfaces).