Проблема с JTable — удаление отфильтрованной таблицы

#java #swing #filter #jtable #rowdeleting

#java #swing #Фильтр #jtable #удаление строк

Вопрос:

Я делаю проект на Java и использую JTable со столбцом с кнопками для удаления строки. Это работает хорошо. После этого я добавил текстовое поле для фильтрации планшета и работы. Проблема заключалась в том, что я пытался использовать 2 вещи. Я ищу строку и отображаю только эту строку, затем я нажимаю кнопку удаления в этой строке, программа удаляет неправильную строку и выдает ошибку. Может кто-нибудь показать мой пример, как это можно сделать? Извините за мой плохой английский.

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

Изображение на лицевой стороне
Изображение ошибки

Ответ №1:

Проблема заключалась в том, что я пытался использовать 2 вещи. Я ищу строку и отображаю только эту строку, затем я нажимаю кнопку удаления в этой строке, программа удаляет неправильную строку

Вам нужно понимать разницу между «моделью» и «представлением».

Данные в модели НЕ фильтруются.

В представлении отображаются только отфильтрованные данные.

Таким образом, у вас может быть 20 строк данных в модели, но только 3 строки данных в представлении.

Поэтому индекс строки между моделью и представлением может отличаться.

Чтобы удалить строку из модели, вам нужно преобразовать индекс из представления в модель, используя что-то вроде:

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

Обратите внимание, что при сортировке применяется та же логика. Данные в модели НЕ отсортированы, данные в представлении отображаются в отсортированном порядке, поэтому индекс также необходимо преобразовать.

Редактировать:

я думаю, что проблема заключается в кнопках внутри JTable.

См.: Кнопка таблицы Coumn для многократно используемой реализации, которая позволяет вам просто указать действие для вызова в выбранной строке. Обратите внимание, вам все равно нужно будет преобразовать индекс в индекс модели.

Комментарии:

1. Я пробовал и не работал. Я добавил весь код и изображения интерфейса и ошибок. я действительно не очень хорошо понимаю JTable.

2. я добавил кнопку вне JTable и могу удалить без проблем. я думаю, что проблема заключается в кнопках внутри JTable. Возможно, я допустил ошибку.

3. @LuisCruz, использование кнопки в качестве редактора не является прямым. Один из подходов см. в разделе Редактирование.

4. @LuisCruz, рад, что это помогло. Не забудьте «принять» ответ, нажав на галочку (рядом с ответом), чтобы люди знали, что проблема решена.