JTable problem - deleting a filtered table

297 Views Asked by At

I am doing a project in Java and use a jTable with a column with Buttons to delete the row. This work well. After that i added a textfield to filter the tablet and work too. The problema was when i try to use the 2 things. I search for a row and only display that row, Next i click on delete Button in that row, the programa delete the wrong row and give a error. Can someone show my a example how can do that? Sorry for my poor english.

package problem;

import java.util.ArrayList;
import javax.swing.JCheckBox;
import javax.swing.JTable;
import javax.swing.RowFilter;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.TableRowSorter;

public class ProblemGUI extends javax.swing.JFrame {
    private TableModel model;
    private ArrayList<Product> products;
    private JTable table;
    private TableRowSorter<TableModel> tr;

    public ProblemGUI() {
        initComponents();
        initTable();
        
        jTextField1.getDocument().addDocumentListener(new DocumentListener(){
            @Override
            public void insertUpdate(DocumentEvent e) {
                String text = jTextField1.getText();
                
                if(text.trim().length() == 0){
                    tr.setRowFilter(null);
                }else{
                    tr.setRowFilter(RowFilter.regexFilter("(?i)" + text));
                }
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                String text = jTextField1.getText();
                
                if(text.trim().length() == 0){
                    tr.setRowFilter(null);
                }else{
                    tr.setRowFilter(RowFilter.regexFilter("(?i)" + text));
                }
            }

            @Override
            public void changedUpdate(DocumentEvent de) {
                throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
            }          
        });
    }
    
    private void initTable(){
        products = new ArrayList();
        products.add(new Product(1, "Product 1"));
        products.add(new Product(2, "Product 2"));
        products.add(new Product(3, "Product 3"));
        
        model = new TableModel(products);
        table = new JTable(model);
        
        tr = new TableRowSorter<>(model);
        table.setRowSorter(tr);
        
        table.getColumn("Remove").setCellRenderer(new ButtonRenderer());
        table.getColumn("Remove").setCellEditor(new ButtonEditor(new JCheckBox(), tr));
               
        jScrollPane1.setViewportView(table);
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        jTextField1 = new javax.swing.JTextField();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane1)
                    .addComponent(jTextField1, javax.swing.GroupLayout.DEFAULT_SIZE, 517, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(4, 4, 4)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 312, Short.MAX_VALUE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>                        

   
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(ProblemGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(ProblemGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(ProblemGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(ProblemGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new ProblemGUI().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify                     
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTextField jTextField1;
    // End of variables declaration                   
}
package problem;

public class Product {
    private int cod;
    private String name;
    private static final String remove = "Remover";
    
    public Product(int cod, String name){
        this.cod = cod;
        this.name = name;
        
    }
    
    public int getCod(){ return cod; }
    public void setCod(int cod){ this.cod = cod; }
    
    public String getName(){ return name; }
    public void setName(String name){ this.name = name; }
    
    public String getRemove(){ return remove; }
}
package problem;

import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;


public class TableModel extends AbstractTableModel{
    private static final int COL_COD = 0;
    private static final int COL_NAME = 1;
    private static final int COL_REMOVE = 2;
    
    private static final String[] headers = new String[]{"Cod", "Name", "Remove"};
    private static final boolean[] permitions = new boolean[]{false, false, true};
    private ArrayList<Product> products;
    
    public TableModel(ArrayList<Product> products){
        this.products = products;
    }

    @Override
    public int getRowCount() { return products.size(); }

    @Override
    public int getColumnCount() { return headers.length; }
    
    @Override
    public Object getValueAt(int row, int col) {
        switch (col){
            case COL_COD:
                return products.get(row).getCod();
            case COL_NAME:
                return products.get(row).getName();
            case COL_REMOVE:
                return products.get(row).getRemove();
            default:
                break;
        }
        
        return null;
    }
    
     @Override
    public String getColumnName(int index) { return headers[index]; }
    
    @Override
    public boolean isCellEditable(int row, int col) { return permitions[col]; }
    
    @Override
    public Class getColumnClass(int columnIndex) {
        if(columnIndex == COL_COD) {
            return Integer.class;
    }
        return String.class;
    }
    
    @Override
    public void setValueAt(Object value, int row, int col){
        switch (col) {
            case COL_COD:
                products.get(row).setCod((int) value);
                break;
            case COL_NAME:
                 products.get(row).setName(value.toString());
                break;
            default:
                break;
        }
    }
    
    public void removeProduto(int row){
        System.out.println("TableModel:removeProduto Row->" + row);
        products.remove(row);
        fireTableRowsDeleted(row, row);  
    }
    
}
package problem;

import java.awt.Component;
import javax.swing.JButton;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;

public class ButtonRenderer extends JButton implements TableCellRenderer{
    public ButtonRenderer(){
        setOpaque(true);
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object o, boolean isSelected, boolean hasFocus, int row, int col) {
        if(isSelected){
            setForeground(table.getSelectionForeground());
            setBackground(table.getSelectionBackground());
        }else{
            setForeground(table.getForeground());
            setBackground(table.getBackground());
        }
        setText((o == null) ? "" : o.toString());
        return this;
    }  
}
package problem;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.table.TableRowSorter;

public class ButtonEditor extends DefaultCellEditor{
    protected JButton button;
    private String label;
    private boolean isPushed;
    private int row;
    private JTable table;
    private TableRowSorter<TableModel> tr;
    
    public ButtonEditor(JCheckBox jcb, TableRowSorter<TableModel> tr) {
        super(jcb);
        this.tr = tr;
        button = new JButton();
        button.setOpaque(true);
        button.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent ae) {
                fireEditingStopped();
            }   
        });
    }  
        
    public Component getTableCellEditorComponent(JTable table, Object o, boolean isSelected, int row, int col){
        if(isSelected){
            button.setForeground(table.getSelectionForeground());
            button.setBackground(table.getSelectionBackground());
        }else{
            button.setForeground(table.getForeground());
            button.setBackground(table.getBackground());
        }
        label = (o == null) ? "" : o.toString();
        button.setText(label);
        isPushed = true;
        this.row = row;
        this.table = table;
        
        return button;
    }
    
    public Object getCellEditorValue(){
        if(isPushed){
            System.out.println("Row: " + row);
            System.out.println("Convert Row: " + table.convertRowIndexToModel(row));
            ((TableModel) table.getModel()).removeProduto(table.convertRowIndexToModel(row));
            JOptionPane.showMessageDialog(button, "Produto removido.");
            
        }
        isPushed = false;
        
        return label;
    }
    
    public boolean stopCellEditing(){
        isPushed = false;
        
        return super.stopCellEditing();
    }
    
    protected void fireEditingStopped(){
        System.out.println("FIRE EDITING STOPPED START");
        super.fireEditingStopped();
        
    }
}

Inface Image Error Image

1

There are 1 best solutions below

4
On

The problema was when i try to use the 2 things. I search for a row and only display that row, Next i click on delete Button in that row, the programa delete the wrong row

You need to understand the difference between the "model" and the "view".

The data in the model is NOT filtered.

Only the filtered data is displayed in the view.

So you may have 20 rows of data in the model, but only 3 rows of data in the view.

Therefore the row index between the model and view may be different.

To delete a row from the model you need to convert the index from the view to the model using something like:

int modelRow = table.convertRowIndexToModel( table.getSelectedRow() );
model.removeRow( modelRow );

Note, the same logic applies when sorting. The data in the model is NOT sorted, the data in the view is displayed in a sorted order so the index will need to be converted as well.

Edit:

i think the problem is the buttons inside the jTable.

See: Table Button Coumn for a reusable implementation that allows you to just specify an Action to invoke on the selected row. Note, you will still need to convert the index to the model index.