Last Row Always Removed from Defaulttablemodel, Regardless of Index

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() is true, setModel() tries to restore the RowSorter created using the old model.

  • You can specify setAutoCreateRowSorter(true) after you change models, as shown below, or extend your TableModel to update the model in place, as shown here.

  • Use ActionListener for JButton instead of MouseListener.

  • 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



Leave a reply



Submit