Last row always removed from DefaultTableModel, regardless of index
The row
obtained from columnAtPoint()
is in view coordinates, while removeRow()
assumes model coordinates. Quoting from the relevant tutorial section:
This distinction does not matter unless your viewed data has been rearranged by sorting, filtering, or user manipulation of columns.
If so, you will need to use convertRowIndexToModel()
, described near the end of Sorting and Filtering, which advises:
When using a sorter, always remember to translate cell coordinates.
Also, consider using a ListSelectionListener
instead of a MouseAdapter
.
Addendum: Your implementation of getValueAt()
continued to access the array supplied to the constructor, while the model's data was actually stored in the parent implementation. If you really need more control over the model's data structure, extend AbstractTableModel
, as shown here.
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
/** @see https://stackoverflow.com/a/11237205/230513 */
public class MainJF extends JFrame {
private JTable tagsJT;
private TagsSelectionTableModel model;
private static int COLUMN_CHECK = 0;
private static int COLUMN_TAG_NAME = 1;
private static int COLUMN_TAG_ID = 2;
private static int COLUMN_EDIT_TAG = 3;
private static int COLUMN_DELETE_TAG = 4;
public MainJF() {
this.add(new JScrollPane(createTagsTable()));
pack();
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private JTable createTagsTable() {
String[] columnNames = {"0", "Tag", "2", "3", "4"};
Object[][] data = new Object[10][columnNames.length];
for (int i = 0; i < data.length; i++) {
data[i][COLUMN_CHECK] = false;
data[i][COLUMN_TAG_NAME] = "Tag " + i;
data[i][COLUMN_TAG_ID] = i;
data[i][COLUMN_EDIT_TAG] = "edit";
data[i][COLUMN_DELETE_TAG] = "delete";
}
model = new TagsSelectionTableModel(columnNames, data);
tagsJT = new JTable(model);
tagsJT.setRowSelectionAllowed(true);
tagsJT.addMouseListener(new TagsTableMA());
return tagsJT;
}
class TagsSelectionTableModel extends DefaultTableModel {
public TagsSelectionTableModel(String[] columnNames, Object[][] data) {
super(data, columnNames);
}
}
class TagsTableMA extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
int row = tagsJT.rowAtPoint(p);
int col = tagsJT.columnAtPoint(p);
if (col == COLUMN_DELETE_TAG) {
model.removeRow(row);
}
}
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new MainJF().setVisible(true);
}
});
}
}
JTable.getSelectedRow() is returning -1
Get the row index using the event, not the table selection:
final int selectedRowIndex = table.rowAtPoint(mouseEvent.getPoint());
// If the rows are sorted or filtered
final int modelRowIndex = table.convertRowIndexToModel(selectedRowIndex);
getSelectedRow()
would not work with multiple selected rows (multiple selections allowed), as it will always return "the index of the first selected row".
Using fireTableDataChanged to Update Table Data
Because DefaultTableModel
uses convertToVector()
internally, updating the clickedData
array used to create model2
does not change the content of the model's dataVector
. As a result, fireTableDataChanged()
notifies the listening JTable
, but the model has not changed in the interim. Instead, update model2
via setValueAt()
, which will fire the correct event for you. A similar problem is examined here.
Also consider using a ListSelectionListener
instead of a MouseListener
.
Calling Previous Model Behavior of JTable.setModel()
Starting from this example, I saw no unexpected behavior with the getColumnClass()
implementation shown below.
private Model() {
final Object[] data = {this.toString()};
this.model = new DefaultTableModel(data, 1){
@Override
public Class<?> getColumnClass(int columnIndex) {
System.out.println(data[0]);
return super.getColumnClass(columnIndex);
}
};
model.addRow(data);
}
Note that JTable
may invoke getColumnClass()
anytime it determines that a cell needs rendering. If necessary, you can use EventQueue.invokeLater()
to schedule something that will "happen after all pending events are processed."
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
// do something
}
});
Addenda:
If
getAutoCreateRowSorter()
istrue
,setModel()
tries to restore theRowSorter
created using the old model.You can specify
setAutoCreateRowSorter(true)
after you change models, as shown below, or extend yourTableModel
to update the model in place, as shown here.Use
ActionListener
forJButton
instead ofMouseListener
.Use a
TableCellEditor
for buttons in the table, as shown here and here.
Code:
addButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (table.getModel().getRowCount() == 2) {
return;
}
Object[][] data = new Object[table.getModel().getRowCount() + 1][5];
table.setRowSorter(null);
for (int i = 0; i <= table.getModel().getRowCount(); i++) {
data[i][0] = i;
data[i][6] = "User" + i;
data[i][7] = "Delete";
}
table.setModel(new CustModel(data));
table.setAutoCreateRowSorter(true);
}
});
JTable not returning selected row correctly
you shouldnt reinitialize your table with a new JTable after you call replace. the fireTableDataChanged() method will alert your existing table that it should repaint. what is happening is that you are looking at the table that you put into the panel, but you are changing the variable to a different instance of JTable. When you query that new, but not visible table, it will give you -1 for the selected row count. it might be helpful if you edit your post to display what is going on in that area of the code.
2nd edit:
instead of this:
if(model==null)
model = new AchievementTableModel(cells, columns);
else
model.replace(cells, columns);
if(table==null) {
table = new JTable(model);
table.setFillsViewportHeight(true);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.getTableHeader().setReorderingAllowed(false);
table.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
table.getColumnModel().setColumnSelectionAllowed(false);
table.getTableHeader().setResizingAllowed(false);
} else
table.setModel(model);
column = table.getColumn(columns[0]);
column.setPreferredWidth(25);
column = table.getColumn(columns[1]);
column.setPreferredWidth(225);
column = table.getColumn(columns[2]);
column.setPreferredWidth(40);
table.doLayout();
add(new JScrollPane(table), BorderLayout.CENTER);
do this instead:
if(model==null) {
model = new AchievementTableModel(cells, columns);
} else {
model.setDataVector(cells, columns);
}
if(table==null) {
table = new JTable(model);
table.setFillsViewportHeight(true);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.getTableHeader().setReorderingAllowed(false);
table.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
table.getColumnModel().setColumnSelectionAllowed(false);
table.getTableHeader().setResizingAllowed(false);
column = table.getColumn(columns[0]);
column.setPreferredWidth(25);
column = table.getColumn(columns[1]);
column.setPreferredWidth(225);
column = table.getColumn(columns[2]);
column.setPreferredWidth(40);
table.doLayout();
add(new JScrollPane(table), BorderLayout.CENTER);
} else {
table.setModel(model);
}
you dont need to add the table to a new scrollpane and re-add it to the panel on each model change.
Calculating total value in JTable Column using new value
As shown in DependentColumn
, you can calculate derived values in your implementation of getValueAt()
. If a column on which the value depends is editable, be sure to fire an event indicating that the dependent cell has changed.
Addendum: I’ve added my code with getValueAt()
, but the result is the same.
Absent your sscce I'm guessing: I see that you access the allData
array after you use it to construct and update the DefaultTableModel
. I'm not sure about the goal of the exercise, but you probably wan't to update the array before doing so. DefaultTableModel
uses Vector
internally, and it ignores the array after construction completes.
Related Topics
Correct Way of Throwing Exceptions with Reactor
What Is the Equivalent of Java Static Methods in Kotlin
How Does the Spring @Responsebody Annotation Work
Differencebetween Iterator and Iterable and How to Use Them
Is It Safe to Get Values from a Java.Util.Hashmap from Multiple Threads (No Modification)
Is This the Best Way to Rewrite the Content of a File in Java
How to Deserialize the Object, If It Was Moved to Another Package or Renamed
What's the Difference Between # , % and $ Signs in Struts Tags
Get First Date of Current Month in Java
Resultset.Getstring(1) Throws Java.Sql.Sqlexception: Invalid Operation at Current Cursor Position
Converting String to "Character" Array in Java
Selenium Webdriver: Wait for Complex Page with JavaScript to Load
Java Read Large Text File with 70Million Line of Text
Java: Object to Byte[] and Byte[] to Object Converter (For Tokyo Cabinet)
How to Define a Method Which Takes a Lambda as a Parameter in Java 8