2013-04-20 102 views
2

美好的一天大家,过滤后的问题JTable

我遇到了一个正在处理的程序问题。简要解释一下,我有一个带有多列和多行的JTable。当改变值时,特定列具有可编辑字段,其他列值根据输入数据而改变。一切正常,但是当我向JTable添加过滤器选项时,对可编辑列进行的更改不会在应用过滤器后按预期更改其他列的值。我附上了一些图片来展示问题。

第一张图显示未过滤的表格正常工作。更改折扣列值将会降低GPL列中存储的相应价格,以输入折扣的百分比为单位,并显示在SP列的相应行中。更改数量列值将相应的SP列价格乘以输入数量,并显示在“总计”列中的相应行中。

Unfiltered table http://i36.tinypic.com/2pq7r50.png

第二图像显示为目的的过滤表不工作。更改折扣或数量列中的值不会更改预期的列。

Filtered table http://i33.tinypic.com/25hcr35.png

我已经添加了SSCCE代码低于其中包含2类。首先是表格本身,其次是表格的听众。

编辑我已经根据camickr的回答改变了该类的代码,现在完全有效。

TableCellChange类

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.GridBagConstraints; 
import java.awt.GridBagLayout; 
import java.awt.Insets; 
import java.awt.KeyboardFocusManager; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.KeyAdapter; 
import java.awt.event.KeyEvent; 
import java.awt.event.WindowAdapter; 
import java.awt.event.WindowEvent; 
import java.beans.PropertyChangeEvent; 
import java.beans.PropertyChangeListener; 
import java.math.BigDecimal; 
import java.math.MathContext; 
import java.text.DecimalFormat; 
import java.util.Locale; 
import javax.swing.AbstractAction; 
import javax.swing.Action; 
import javax.swing.DefaultCellEditor; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JOptionPane; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.JTextField; 
import javax.swing.RowFilter; 
import javax.swing.SwingUtilities; 
import javax.swing.UIManager; 
import javax.swing.event.DocumentEvent; 
import javax.swing.event.DocumentListener; 
import javax.swing.table.DefaultTableModel; 
import javax.swing.table.TableColumn; 
import javax.swing.table.TableModel; 
import javax.swing.table.TableRowSorter; 

public final class TableCellChange extends JPanel { 

    private static JFrame frameTableCellChange; 
    private JPanel panelTable, panelButtons; 
    private JButton buttonResetDiscounts, buttonResetQuantities, buttonExit; 
    private JTextField textFilterBox, quantityField, discountField; 
    private JLabel labelFilter; 
    private DefaultTableModel tableModel; 
    private JTable table; 
    private TableRowSorter<DefaultTableModel> sorter; 
    private TableColumn columnDiscount, columnTotal, columnQuantity; 
    private TableCellListener tableCellListener; 
    private String checkForNull; 
    private DecimalFormat decimalFormatUS; 
    private Locale localeUSFormat;  
    private BigDecimal valueDiscount, valueGPL, resultDiscount, resultSP, resultTotal, 
      backupDiscount = new BigDecimal("0"); 
    private int selectedColumnIndex, selectedRowIndex, valueQuantity, backupQuantity = 1; 

    public TableCellChange() { 

     super(); 

     panelTable = new JPanel(); 
     panelButtons = new JPanel(); 
     setLayout(new BorderLayout()); 

     createTable(); 
     createButtons(); 
     add(panelTable, BorderLayout.NORTH); 
     add(panelButtons, BorderLayout.CENTER); 

     // Always focus on the JTextField when opening the window 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       textFilterBox.requestFocusInWindow(); 
      } 
     }); 
    } // -> TableCellChange() 

    // Create the buttons for the query result window 
    public void createButtons() { 
     GridBagLayout gridbag = new GridBagLayout(); 
     GridBagConstraints gridcons = new GridBagConstraints(); 
     gridcons.fill = GridBagConstraints.HORIZONTAL; 
     panelButtons.setLayout(gridbag); 

     labelFilter = new JLabel("Quick search:"); 
     gridcons.insets = new Insets(5,0,0,0); 
     gridcons.gridx = 0; 
     gridcons.gridy = 0; 
     gridcons.gridwidth = 2; 
     gridbag.setConstraints(labelFilter, gridcons); 
     labelFilter.setHorizontalAlignment(JLabel.CENTER); 
     panelButtons.add(labelFilter); 

     // Create text field for filtering 
     textFilterBox = new JTextField(); 
     gridcons.insets = new Insets(5,0,0,0); 
     gridcons.gridx = 0; 
     gridcons.gridy = 1; 
     gridcons.gridwidth = 2; 
     gridbag.setConstraints(textFilterBox, gridcons); 
       textFilterBox.getDocument().addDocumentListener(
       new DocumentListener() { 
        @Override 
        public void changedUpdate(DocumentEvent e) { 
         tableFilter(); 
        } 
        @Override 
        public void insertUpdate(DocumentEvent e) { 
         tableFilter(); 
        } 
        @Override 
        public void removeUpdate(DocumentEvent e) { 
         tableFilter(); 
        } 
       }); // -> DocumentListener() 
     panelButtons.add(textFilterBox); 

     // Create the button to reset the discount column to 0% 
     buttonResetDiscounts = new JButton("Reset all discounts"); 
     buttonResetDiscounts.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) 
      {   
       BigDecimal valueGPL, valueTotal; 
       int valueQuantity; 
       for (int i = 0; i < table.getModel().getRowCount(); i++) { 
        valueGPL = new BigDecimal(table.getModel(). 
          getValueAt(i, 2).toString().replaceAll("[$,]", "")); 
        table.getModel().setValueAt("0%", i, 3); 
        table.getModel().setValueAt(DecimalFormat 
          .getCurrencyInstance(localeUSFormat).format(valueGPL), i, 4); 
        valueQuantity = Integer.parseInt(table.getModel(). 
          getValueAt(i, 5).toString()); 
        valueTotal = valueGPL.multiply(new BigDecimal(valueQuantity), 
          new MathContext(BigDecimal.ROUND_HALF_EVEN)); 
        table.getModel().setValueAt(DecimalFormat 
          .getCurrencyInstance(localeUSFormat).format(valueTotal), i, 6); 
       } 
      } 
     });   
     gridcons.insets = new Insets(10,0,0,0); 
     gridcons.gridx = 0; 
     gridcons.gridy = 3; 
     gridcons.gridwidth = 1; 
     gridbag.setConstraints(buttonResetDiscounts, gridcons); 
     panelButtons.add(buttonResetDiscounts); 

     // Create button to reset the quantity column to 1 
     buttonResetQuantities = new JButton("Reset all quantities"); 
     buttonResetQuantities.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) 
      {    
       BigDecimal valueSP; 
       for (int i = 0; i < table.getModel().getRowCount(); i++) {      
        valueSP = new BigDecimal(table.getModel(). 
          getValueAt(i, 4).toString().replaceAll("[$,]", "")); 
        table.getModel().setValueAt("1", i, 5); 
        table.getModel().setValueAt(DecimalFormat. 
          getCurrencyInstance(localeUSFormat).format(valueSP), i, 6); 
       } 
      }   
     });   
     gridcons.insets = new Insets(10,0,0,0); 
     gridcons.gridx = 1; 
     gridcons.gridy = 3; 
     gridcons.gridwidth = 1; 
     gridbag.setConstraints(buttonResetQuantities, gridcons); 
     panelButtons.add(buttonResetQuantities); 

     // Create button for closing the window and releasing resources 
     buttonExit = new JButton("Exit"); 
     buttonExit.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) 
      {     
       System.exit(0); 
      } 
     }); 
     gridcons.insets = new Insets(5,0,0,0); 
     gridcons.gridx = 0; 
     gridcons.gridy = 5; 
     gridcons.gridwidth = 2; 
     gridbag.setConstraints(buttonExit, gridcons); 
     panelButtons.add(buttonExit);  

    } // -> createButtons() 


    // Filters the JTable based on user input 
    private void tableFilter() { 
     RowFilter<DefaultTableModel, Object> tableRowFilter;// = null; 
     // If current expression doesn't parse, don't update 
     try { 
      tableRowFilter = RowFilter.regexFilter("(?i)" + textFilterBox. 
        getText(), 0, 1, 2); 
     } catch (java.util.regex.PatternSyntaxException e) { 
      return; 
      } 
     sorter.setRowFilter(tableRowFilter);  
    } // -> tableFilter 

    // Method that creates the JTable 
    public void createTable() { 

     // Create listener for selecting all text when a text field gains focus 
     KeyboardFocusManager.getCurrentKeyboardFocusManager() 
     .addPropertyChangeListener("permanentFocusOwner", new PropertyChangeListener() { 
     @Override 
      public void propertyChange(final PropertyChangeEvent e) { 
       if (e.getNewValue() instanceof JTextField) { 
        SwingUtilities.invokeLater(new Runnable() { 
         @Override 
         public void run() { 
          JTextField textField = (JTextField)e.getNewValue(); 
          textField.selectAll(); 
         } 
        }); 
       } 
      } 
     }); 

     String[] columnNames = {"Model", "Description", "GPL", "Discount", "SP", 
       "Quantity", "Total"}; 

     Object[][] data = { 
      {"MR16", "desc1", "$649.00", "0%", "$649.00", new Integer(1), "$649.00"}, 
      {"MR24", "desc2", "$1,199.00", "0%", "$1,199.00", new Integer(1), "1,199.00"}, 
      {"MR62", "desc3", "$699.00", "0%", "$699.00", new Integer(1), "$699.00"}, 
      {"MR66", "desc4", "$1,299.00", "0%", "$1,299.00", new Integer(1), "$1,299.00"}, 
      {"MX80", "desc5", "$1,995.00", "0%", "$1,995.00", new Integer(1), "$1,995.00"}, 
      {"MX90", "desc6", "$3,995.00", "0%", "$3,995.00", new Integer(1), "$3,995.00"}, 
      {"MX400", "desc7", "$15,995.00", "0%", "$15,995.00", new Integer(1), "$15,995.00"}, 
      {"MX600", "desc8", "$31,995.00", "0%", "$31,995.00", new Integer(1), "$31,995.00"}, 
      {"MS22-HW", "desc9", "$1,999.00", "0%", "$1,999.00", new Integer(1), "$1,999.00"}, 
      {"MS42-HW", "desc10", "$3,499.00", "0%", "$3,499.00", new Integer(1), "$3,499.00"}, 

     }; 

     // Create the TableModel and populate it 
     tableModel = new DefaultTableModel(data, columnNames) { 
      Class [] classes = {String.class, String.class, String.class, 
       String.class, String.class, int.class, String.class, Boolean.class};     
      @Override 
      public Class getColumnClass(int column) { 
       return classes[column]; 
      } 
     }; 

     // Create a JTable and populate it with the content of the TableModel 
     table = new JTable(tableModel) { 
      @Override 
      public boolean isCellEditable(int row, int column) { 
       if (column == 0 || column == 1 || column == 2 || column == 4 || 
         column == 6) { 
        return false; 
       } 
       return true; 
      } 
     }; 

     // This sorter is used for text filtering 
     sorter = new TableRowSorter<>(tableModel); 
     for (int column = 3; column < 6; column++) { 
      sorter.setSortable(column, false); 
     } 
     table.setRowSorter(sorter); 

     columnTotal= table.getColumnModel().getColumn(6); 
     columnTotal.setPreferredWidth(100); 

     // Filter user input in the quantity text field to only allow digits 
     discountField =new JTextField(); 
     discountField.addKeyListener(new KeyAdapter() { 
      @Override 
      public void keyTyped(KeyEvent e) 
      { 
       if(!Character.isDigit(e.getKeyChar()) && e.getKeyChar() !=KeyEvent.VK_BACK_SPACE) { 
        discountField.setEditable(false); 
        discountField.setBackground(Color.WHITE); 
       } else { 
        discountField.setEditable(true); 
       } 
      } 
     }); 

     // Set the text field to the cells of the quantity column 
     columnQuantity = table.getColumnModel().getColumn(5); 
     columnQuantity.setCellEditor(new DefaultCellEditor (discountField)); 

     // Filter user input in the discount text field to only allow digits 
     quantityField =new JTextField(); 
     quantityField.addKeyListener(new KeyAdapter() { 
      @Override 
      public void keyTyped(KeyEvent e) 
      { 
       if(!Character.isDigit(e.getKeyChar()) && e.getKeyChar() !=KeyEvent.VK_BACK_SPACE) { 
        quantityField.setEditable(false); 
        quantityField.setBackground(Color.WHITE); 
        //JOptionPane.showMessageDialog(null,"Only digit input is allowed!"); 
       } else { 
        quantityField.setEditable(true); 
       } 
      } 
     }); 

     // Set the text field to the cells of the quantity column 
     columnDiscount = table.getColumnModel().getColumn(3); 
     columnDiscount.setCellEditor(new DefaultCellEditor(discountField)); 

     // Create an US number format 
     localeUSFormat = Locale.US; 
     decimalFormatUS = (DecimalFormat) DecimalFormat.getInstance(localeUSFormat); 
     decimalFormatUS.setMaximumFractionDigits(2); 

     // Create abstract action which listens for changes made in the JTable 
     Action actionTableListener = new AbstractAction() { 
      @Override 
      public void actionPerformed(ActionEvent e) { 

       TableCellListener tcl = (TableCellListener)e.getSource(); 
       // Get the current row and column index of the table 
       selectedRowIndex = tcl.getRow(); 
       selectedColumnIndex = tcl.getColumn(); 
       TableModel model = tcl.getTable().getModel(); 

       // Have a string variable check for null cell value 
       checkForNull = model.getValueAt(selectedRowIndex,selectedColumnIndex).toString(); 

       // Change the discounted and total price values 
       if (selectedColumnIndex == 3) { 

        // Check if the discount value is null and replace with 
        // last used value if true     
        if (checkForNull.equals("")) { 
         model.setValueAt(backupDiscount + "%",selectedRowIndex, selectedColumnIndex); 
         return; 
        } 

        // Get the discount value and replace the '%' with nothing 
        valueDiscount = new BigDecimal((model 
          .getValueAt(selectedRowIndex,selectedColumnIndex) 
          .toString().replaceAll("[%]",""))); 
        // 
        model.setValueAt(valueDiscount + "%",selectedRowIndex, selectedColumnIndex); 

        // Check if the discount value is greater than 100 
        if ((valueDiscount.compareTo(new BigDecimal(100)) == 1)) { 
         model.setValueAt(backupDiscount + "%",selectedRowIndex, selectedColumnIndex); 
         JOptionPane.showMessageDialog(null,"Discount cannot be more than 100%."); 
        } else { 
         backupDiscount = valueDiscount; 
         valueDiscount = valueDiscount.divide(new BigDecimal(100) 
           , 2, BigDecimal.ROUND_HALF_EVEN); 

         // Calculate SP and Total values based on the discount input 
         valueGPL = new BigDecimal((model 
           .getValueAt(selectedRowIndex,selectedColumnIndex - 1) 
           .toString().replaceAll("[$,]",""))); 
         // Get the quantity value 
         valueQuantity = Integer.parseInt((model 
           .getValueAt(selectedRowIndex,selectedColumnIndex + 2) 
           .toString())); 
         // Calculate the new discount value 
         resultDiscount = valueGPL.multiply(valueDiscount, 
           new MathContext(BigDecimal.ROUND_HALF_EVEN)); 
         // Calculate the new SP value 
         resultSP = valueGPL.subtract(resultDiscount, 
           new MathContext(BigDecimal.ROUND_HALF_EVEN)); 
         // Calculate the new result value 
         resultTotal = resultSP.multiply(new BigDecimal(valueQuantity), 
           new MathContext(BigDecimal.ROUND_HALF_EVEN)); 
         // Display the new SP value 
         model.setValueAt(DecimalFormat.getCurrencyInstance(localeUSFormat) 
           .format(resultSP),selectedRowIndex, selectedColumnIndex + 1); 
         // Display the new Total value 
         model.setValueAt(DecimalFormat.getCurrencyInstance(localeUSFormat) 
           .format(resultTotal),selectedRowIndex, selectedColumnIndex + 3); 
         } 
       } 
       // Change the total price values based on the quantity column 
       if (selectedColumnIndex == 5) { 
        // Check if the quantity value is null and replace with 
        // last used value if true 
        if (checkForNull.equals("")) { 
         model.setValueAt(backupQuantity,selectedRowIndex, selectedColumnIndex); 
         return; 
        } 

        // Change total price value based on the quantity column 
        resultSP = new BigDecimal((model. 
          getValueAt(selectedRowIndex, 
          selectedColumnIndex - 1).toString().replaceAll("[$,]",""))); 
        valueQuantity = Integer.parseInt((model.getValueAt(selectedRowIndex, 
          selectedColumnIndex).toString())); 

        // Check if the value quantity is over a certain limit 
        if (valueQuantity <= 0 || valueQuantity >= 999999) { 
         model.setValueAt(backupQuantity,selectedRowIndex, selectedColumnIndex); 
         JOptionPane.showMessageDialog(null,"Quantity value is too high or invalid!"); 
        } else { 

         // If the value is under the limit: backup the new quantity 
         // value, calculate the new total value and display it 
         backupQuantity = valueQuantity; 
         resultTotal = resultSP.multiply(new BigDecimal(valueQuantity), 
           new MathContext(BigDecimal.ROUND_HALF_EVEN)); 
         model.setValueAt(DecimalFormat.getCurrencyInstance(localeUSFormat) 
           .format(resultTotal), selectedRowIndex, selectedColumnIndex + 1); 
         } 
       } 

      } 
     }; // -> AbstractAction() 

     tableCellListener = new TableCellListener(table, actionTableListener); 
     table.setPreferredScrollableViewportSize(table. 
       getPreferredSize());  
     table.setRowHeight(22); 

     setVisibleRowCount(table,10); 

     table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 
     table.setFillsViewportHeight(true); 
     table.getTableHeader().setReorderingAllowed(false); 
     table.getTableHeader().setResizingAllowed(false); 
     panelTable.add(new JScrollPane(table)); 
    } // -> createTable() 

    // Method to display a fixed number of rows in the JTable viewport 
    public static void setVisibleRowCount(JTable table, int rows){ 
     int height = 0; 
     for(int row=0; row<rows; row++) { 
      height += table.getRowHeight(row); 
     } 
     table.setPreferredScrollableViewportSize(new Dimension( 
      table.getPreferredScrollableViewportSize().width, height)); 
    } 

     // Create and display the contents of the frame 
     public static void showGUI() { 
     // Disable boldface controls 
     UIManager.put("swing.boldMetal", Boolean.FALSE); 

     // Create the frame 
     frameTableCellChange = new JFrame("Table frame"); 
     frameTableCellChange.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
     frameTableCellChange.addWindowListener(new WindowAdapter() { 
      @Override 
       public void windowClosing(WindowEvent we) { 
       System.exit(0); 
      } 
     }); 

     // Create and set up the content pane. 
     TableCellChange newContentPane = new TableCellChange(); 
     newContentPane.setOpaque(true); //content panes must be opaque 
     frameTableCellChange.setContentPane(newContentPane); 

     // Arrange and display the window. 
     frameTableCellChange.pack(); //must be called first 
     frameTableCellChange.setLocationRelativeTo(null); //center window 
     frameTableCellChange.setResizable(false); 
     frameTableCellChange.setVisible(true);   
     } //-> showQueryResultGUI() 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
     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 | InstantiationException | 
       IllegalAccessException | 
       javax.swing.UnsupportedLookAndFeelException ex) { 
      java.util.logging.Logger.getLogger(TableCellChange.class.getName()). 
        log(java.util.logging.Level.SEVERE, null, ex); 
     } 
     // Display the frame and it's contents  
     TableCellChange.showGUI(); 
      } 
     }); 
    } //-> main(String[] args)  

} //-> TableCellChange class 

编辑这个类是由罗布Camick(又名camickr)创建,所有学分去他创造了这真棒一段代码。为了尊重字符限制,只从代码中删除了注释。

TableCellListener类

import java.awt.event.ActionEvent; 
import java.beans.PropertyChangeEvent; 
import java.beans.PropertyChangeListener; 
import javax.swing.Action; 
import javax.swing.JTable; 
import javax.swing.SwingUtilities; 

/* 
* This class listens for changes made to the data in the table via the 
* TableCellEditor. When editing is started, the value of the cell is saved 
* When editing is stopped the new value is saved. When the old and new 
* values are different, then the provided Action is invoked. 
* The source of the Action is a TableCellListener instance. 
*/ 
public class TableCellListener implements PropertyChangeListener, Runnable { 
    private JTable table; 
    private Action action; 

    private int row; 
    private int column; 
    private Object oldValue; 
    private Object newValue; 

    public TableCellListener(JTable table, Action action) { 
     this.table = table; 
     this.action = action; 

     this.table.addPropertyChangeListener(this); 
    } 

    private TableCellListener(JTable table, int row, int column, Object oldValue, Object newValue) { 
     this.table = table; 
     this.row = row; 
     this.column = column; 
     this.oldValue = oldValue; 
     this.newValue = newValue; 
    } 

    public int getColumn() { 
     return column; 
    } 

    public Object getNewValue() { 
     return newValue; 
    } 

    public Object getOldValue() { 
     return oldValue; 
    } 

    public int getRow() { 
     return row; 
    } 

    public JTable getTable() { 
     return table; 
    } 
    @Override 
    public void propertyChange(PropertyChangeEvent e) { 
     if ("tableCellEditor".equals(e.getPropertyName())) { 
      if (table.isEditing()) { 
       processEditingStarted(); 
      } else { 
       processEditingStopped(); 
      } 
     } 
    } 

    private void processEditingStarted() { 
     SwingUtilities.invokeLater(this); 
    } 
    @Override 
    public void run() { 
     row = table.convertRowIndexToView(table.getEditingRow()); 
     row = table.getEditingRow(); 

     column = table.convertColumnIndexToModel(table.getEditingColumn()); 

     oldValue = table.getModel().getValueAt(row, column); 
     newValue = null; 
    } 

    private void processEditingStopped() { 
     newValue = table.getModel().getValueAt(row, column); 
     if (!newValue.equals(oldValue)) { 

      TableCellListener tcl = new TableCellListener(
       getTable(), getRow(), getColumn(), getOldValue(), getNewValue()); 

      ActionEvent event = new ActionEvent(
       tcl, 
       ActionEvent.ACTION_PERFORMED, 
       ""); 
      action.actionPerformed(event); 
     } 
    } 
} 

据我所知,过滤表时表视图变化的指标,必须与底层模型的索引同步。如何做到这一点,以使过滤的表格工作?

谢谢你的阅读,祝你有美好的一天。 :)

回答

3

您实现了操作的TableCellListener不正确。您不能使用选定的行/列,因为这些值位于表格视图中。 TableCellListener在模型上工作。

查看Table Cell Editor提供的示例操作。要获取已更改的行/列,必须引用TableCellListener本身。

编辑:

这是我的简单文本示例。当您更改“价格”时,“价格更改”和“价值”列会自动更新。

import java.awt.*; 
import java.awt.event.*; 
import java.io.*; 
import java.net.*; 
import javax.swing.*; 
import javax.swing.table.*; 

public class TableCellListenerTest 
{ 
    public static void main(String[] args) 
    { 
     SwingUtilities.invokeLater(new Runnable() { 
      public void run() { 
       createAndShowGUI(); 
      } 
     }); 
    } 

    public static void createAndShowGUI() 
    { 
     String[] columnNames = {"Stock", "Shares", "Price", "Price Change", "Value"}; 
     Object[][] data = 
     { 
      {"IBM", new Integer(100), new Double(85), new Double(0), new Double(8500)}, 
      {"Apple", new Integer(300), new Double(30), new Double(0), new Double(9000)}, 
      {"Sun", new Integer(1500), new Double(5), new Double(0), new Double(7500)}, 
      {"Google", new Integer(100), new Double(100), new Double(0), new Double(10000)} 
     }; 

     DefaultTableModel model = new DefaultTableModel(data, columnNames) 
     { 
      public Class getColumnClass(int column) 
      { 
       return getValueAt(0, column).getClass(); 
      } 

      public boolean isCellEditable(int row, int column) 
      { 
       return column == 2; 
      } 
     }; 

     JTable table = new JTable(model); 
     table.setPreferredScrollableViewportSize(table.getPreferredSize()); 
     JScrollPane scrollPane = new JScrollPane(table); 

     // Add a sorter 

     TableRowSorter<DefaultTableModel> sorter = new TableRowSorter<DefaultTableModel>(model); 
     table.setRowSorter(sorter); 

     // Filter 

     try 
     { 
      RowFilter<DefaultTableModel, Object> rf = RowFilter.regexFilter("l", 0); 
      sorter.setRowFilter(rf); 
     } 
     catch (java.util.regex.PatternSyntaxException e) {} 

     Action action = new AbstractAction() 
     { 
      public void actionPerformed(ActionEvent e) 
      { 
       TableCellListener tcl = (TableCellListener)e.getSource(); 
       int column = tcl.getColumn(); 

       if (column == 2) 
       { 
        int row = tcl.getRow(); 
        double oldPrice = ((Double)tcl.getOldValue()).doubleValue(); 
        double newPrice = ((Double)tcl.getNewValue()).doubleValue(); 
        TableModel model = tcl.getTable().getModel(); 

        double priceChange = new Double(newPrice - oldPrice); 
        model.setValueAt(priceChange, row, 3); 

        double shares = ((Integer)model.getValueAt(row, 1)).doubleValue(); 
        Double value = new Double(shares * newPrice); 
        model.setValueAt(value, row, 4); 
       } 
      } 
     }; 

     TableCellListener tcl = new TableCellListener(table, action); 

     JFrame.setDefaultLookAndFeelDecorated(true); 
     JFrame frame = new JFrame("Table Cell Listener"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.add(scrollPane); 
     frame.setSize(400, 160); 
     frame.setLocationRelativeTo(null); 
     frame.setVisible(true); 
    } 
} 
+0

谢谢你回复camickr。我想为您提供TableCellListener类的功劳,但我忘了链接到您的博客,我很抱歉没有这样做。这在未经过滤的表格上工作得很完美。 我知道这可能对您而言太多了,但您能否告诉我究竟要在代码中添加/更改哪些内容才能使操作适用于已过滤的表格?我仍然对索引转换的事情感到困惑。我也希望这篇文章能够帮助其他人解决同样的问题。 干杯! – K33p3r 2013-04-21 08:37:06

+1

'你能告诉我究竟要在代码中添加/更改什么,才能使动作适用于已过滤的表格吗?“ - 不,因为我不知道你正在尝试做什么。 “我仍然对索引转换事情感到困惑。” - 您无需担心转换。行/列已经为您转换。只需使用TableCellListener类的getColumn()和getRow()方法即可。博客文章向您展示了如何做到这一点。我还添加了我的简单测试代码。不要使用table.getSelectedXXX(...)方法。 – camickr 2013-04-21 18:21:28

+0

我改变了以下内容: 'selectedRowIndex = table.getSelectedRow(); ()TableCellListener tcl =(TableCellListener)e.getSource();返回值为0。 selectedRowIndex = tcl.getRow(); selectedColumnIndex = tcl.getColumn();' 他们在表格上工作正常时,未过滤,但当我过滤它,例如只通过在文本框中输入“MR62”选择第三行结果是一样的第二张图片在我的文章中。当我对表格的一列进行排序时也是如此。 我还在做错了吗? – K33p3r 2013-04-22 06:46:45

4

您可能需要执行从视图到模型的转换。看看的Sorting and Filtering部分如何使用表教程:

当表使用分拣机,用户看到的可能是一个 不同的顺序由数据模型指定的数据,并可而不是 包含数据模型指定的所有行。用户 实际看到的数据被称为视图,并具有其自己的一组 坐标。JTable提供了从模型 坐标转换为视图坐标的方法 - convertColumnIndexToView和 convertRowIndexToView - 并且从视图坐标转换为 模型坐标 - convertColumnIndexToModel和 convertRowIndexToModel。

编辑:从示图(表)转换成模型:

替换这些行:

row = table.convertRowIndexToView(table.getEditingRow()); 
row = table.getEditingRow(); 

有了:

row = table.convertRowIndexToModel(table.getEditingRow()); 
+0

感谢您的回复。我试过了,但没有奏效。你能告诉我将这些方法放在我提交的代码中的位置吗? – K33p3r 2013-04-20 16:47:21

+0

@ K33p3r我无法重现该问题。 – tenorsax 2013-04-20 17:22:23

+0

程序是否成功运行?如果是,请在文本框中输入“模型”列中的其中一个值,然后尝试更改“折扣”或“数量”列中的值。 – K33p3r 2013-04-20 17:32:40