使用不带分组字符(千位分隔符)的数字筛选 JTable



我正在尝试过滤包含数字列的JTable中的行。

到目前为止,过滤工作正常,但它过滤了包括千位分隔符在内的数字。例如,如果一行中有一行数字为 25689,并且我尝试过滤此行,则必须使用"25.689"。因此,似乎在过滤之前执行了格式化。

我尝试设置自己的默认渲染器,并且显示的数字没有分隔符,但过滤是相同的。

编辑

添加了一个完整的示例来重现我的问题:

public class GroupingTest {
    JFrame frame= null;
    Container pane= null;
    JTextField tf=null;
    JXTable table=null;
    public void searchTable() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {                       
                    final String searchEx = "(?i)"
                                          + Pattern.quote(tf.getText());
                    final RowFilter<TableModel, Object> filter;
                    filter = RowFilter.regexFilter(searchEx);    
                    table.setRowFilter(filter);
                    //packAll in edt
                    Utility.packTableView(table);                       
                } catch (final Exception e) {
                    return;
                }
            }
        });
    }
    public void createTable() {
        frame = new JFrame();
        pane=frame.getContentPane();
        tf = new JTextField();
        tf.setPreferredSize(new Dimension(200,25));
        tf.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void removeUpdate(final DocumentEvent e) {
                searchTable();
            }
            @Override
            public void insertUpdate(final DocumentEvent e) {
                searchTable();
            }
            @Override
            public void changedUpdate(final DocumentEvent e) {
                searchTable();
            }
        });
        String[] columnHeaders = {"long","strings"};
        DefaultTableModel $model = new DefaultTableModel(columnHeaders, 0) {
            @Override
            public Class<?> getColumnClass(final int $col) {
                if($col == 0) {
                    return Long.class;
                } else if($col == 1){
                    return String.class;
                } else {
                    return Object.class;
                }
            }
        };
        table = new JXTable($model);
        table.setDefaultRenderer(Long.class, new DefaultTableCellRenderer() {
            @Override
            public java.awt.Component getTableCellRendererComponent(final JTable $table,
                    final Object $value, final boolean $isSelected, final boolean $hasFocus, final int $row,
                    final int $column) {
                super.getTableCellRendererComponent($table, $value, $isSelected, $hasFocus, $row, $column);
                if ($value instanceof Long) {
                    this.setHorizontalAlignment(SwingConstants.RIGHT);
                }
                return this;
            }
        });
        Object[] line1 = {new Long(23345),"asdf"};
        $model.addRow(line1);
        Object[] line2 = {new Long(3),"dfw"};
        $model.addRow(line2);
        pane.add(tf,BorderLayout.NORTH);
        pane.add(new JScrollPane(table),BorderLayout.CENTER);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(300,200));
        frame.pack();
        frame.setVisible(true);             
    }
    public static void main(String[] args) {        
        GroupingTest gt = new GroupingTest();
        gt.createTable();    
    }    
}

到目前为止,过滤仍在工作,但它过滤了包括千位分隔符在内的数字。

当值的格式干扰排序器和过滤器的预期功能时,是时候检查表模型中的 getColumnClass(int columnIndex) 是否正在检索适当的类(在本例中为 Double )。

默认情况下AbstractTableModel此类方法的实现返回Object.classtoString()方法呈现(这就是您看到千位分隔符的原因),并且可能还会根据字符串表示形式进行过滤。AbstractTableModel的子类(如DefaultTableModel)继承了这个实现,因此应该重写这个方法。例如,假设您的表模型是DefaultTableModel,第一列是Double

DefaultTableModel model = new DefaultTableModel()  {
    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return columnIndex == 0 ? Double.class 
                                : super.getColumnClass(columnIndex);
    }
};

有关更多详细信息,请参阅如何使用表教程的排序和筛选部分。

更新

鉴于您的新MVCE,现在很清楚您要实现的目标。我会开始说我错误地假设您的表模型持有Double而不是Long这对覆盖getColumnClass()方法没有区别(无论如何都应该这样做),但它会在最终解决方案中产生轻微差异。

现在,要明确说明要求,您需要过滤该列:

  • 用户输入一个数字(长),包括分组字符。
  • 用户输入的数字对字符进行分组。
  • 值的字符串
  • 表示形式包含用户键入的子字符串。

为了实现这个目标,我会使用自定义RowFilter,而不是像您在示例中那样使用正则表达式过滤器。这是为了控制用户键入的字符串并检查上面列出的三个条件。我已经设法修改了您的searchTable()以满足要求。注意:我在此方法中包含查询的字符串作为参数tf以使文本字段远离实现。请参阅下面的代码:

private void searchTable(final String query) {
    RowFilter<TableModel, Integer> filter = null;
    if (query.length() > 0) {
        filter = new RowFilter<TableModel, Integer>() {
            @Override
            public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
               for (int i = 0; i < entry.getValueCount(); i++) {
                   String stringValue = entry.getStringValue(i);
                   Object entryValue = entry.getValue(i);
                   String numberString = entryValue instanceof Long 
                                       ? String.valueOf(entryValue)
                                       : "";
                   if (stringValue.contains(query) || numberString.contains(query)) {
                       return true;
                   }
               }
               return false;
            }
        };
    }
    table.setRowFilter(filter);
}

流程将或多或少如下所示:

  1. 如果查询长度为 0,则只需null过滤器即可。这意味着不会筛选表,并且将包含所有条目。

  2. 如果不是 (1),则准备一个新的过滤器,该过滤器遍历整行,询问条目的字符串表示形式或条目的字符串值是否包含查询的字符串。虽然这些可能看起来相同,但事实并非如此,因为 Entry#getStringValue(int index) 可能(并且实际上确实)检索与 String#valueOf(entry#getValue(int index))不同的值。在这种情况下,第一个检索 Long 包括分组分隔符(如果您愿意,也可以格式化),而第二个检索完全没有格式的 Long(这意味着没有分组分隔符)。

  3. 在任一情况下,都将筛选器应用于表。

我希望这个想法足够清楚。如果要过滤Double则必须对其进行一些调整,因为String.valueOf(double)包含小数(不分组)分隔符,您可能希望在检查它是否包含查询的字符串之前将其删除。

最新更新