2015-05-26 2 views
7

Я пытаюсь создать фильтр строк для JTable, чтобы ограничить количество строк, отображаемых в таблице.Как работает JTable RowFilter?

Код RowFilter прост. Он преобразует номер модели строки к числу вида строки (в случае, если таблица отсортирована), а затем проверяет, если число вида строки меньше, что количество строк, которое будет отображаться в таблице:

RowFilter<TableModel, Integer> filter = new RowFilter<TableModel, Integer>() 
{ 
    @Override 
    public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry) 
    { 
     int modelRow = entry.getIdentifier(); 
     int viewRow = table.convertRowIndexToView(modelRow); 

     return viewRow < numberOfRows; 
    } 

}; 

Проблемы заключается в том, что номер строки модели не всегда преобразуется в разумный номер строки просмотра, поэтому слишком много строк включаются в фильтр. Для демонстрации выполнения кода ниже:

1) Выберите «1» из выпадающего списка, и вы получите выход как:

Change the Filter to: 1 
m0 : v0 
m1 : v0 
m2 : v0 
m3 : v0 
m4 : v0 

Этот вывод говорит мне, что все модели строки преобразовываются для просмотра строки 0. Так как 0 меньше значения фильтра 1, все строки включены в фильтр (что неверно).

Итак, вопрос в том, почему convertRowIndexToView(modelRow) работает не так, как ожидалось?

2) Теперь выберите «2» из выпадающего списка, и вы получите выход, как:

Change the Filter to: 2 
m0 : v0 
m1 : v1 
m2 : v2 
m3 : v3 
m4 : v4 

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

3) Теперь выберите «3» из выпадающего списка, и вы получите выход, как:

Change the Filter to: 3 
m0 : v0 
m1 : v1 
m2 : v-1 
m3 : v-1 
m4 : v-1 

В этом случае последние 3 модели строки преобразуются в -1, который я предполагаю, означает, что строка в настоящее время не отображается в таблице, что является правильным. Таким образом, в этом случае все 5 строк снова включаются в фильтр, который неверен, так как мы хотим только первого. 3.

Итак, вопрос в том, как сбросить фильтр, чтобы все строки модели отображались в исходную строку представления?

Я пытался использовать:

((TableRowSorter) table.getRowSorter()).setRowFilter(null); 
((TableRowSorter) table.getRowSorter()).setRowFilter(filter); 

, чтобы очистить фильтр, перед сбросом фильтра, но это дало те же результаты, как шаг 1. То есть, теперь все модели строк на карте, чтобы просмотреть строку 0 (так все 5 строк все еще отображаются).

Вот код теста:

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

public class FilterSSCCE extends JPanel 
{ 
    private JTable table; 

    public FilterSSCCE() 
    { 
     setLayout(new BorderLayout()); 

     JComboBox<Integer> comboBox = new JComboBox<Integer>(); 
     comboBox.addItem(new Integer(1)); 
     comboBox.addItem(new Integer(2)); 
     comboBox.addItem(new Integer(3)); 
     comboBox.addItem(new Integer(4)); 
     comboBox.addItem(new Integer(5)); 
     comboBox.setSelectedIndex(4); 

     comboBox.addActionListener(new ActionListener() 
     { 
      @Override 
      public void actionPerformed(ActionEvent e) 
      { 
       //System.out.println(table.convertRowIndexToView(4)); 
       Integer value = (Integer)comboBox.getSelectedItem(); 
       newFilter(value); 
       //System.out.println(table.convertRowIndexToView(4)); 
      } 
     }); 
     add(comboBox, BorderLayout.NORTH); 

     table = new JTable(5, 1); 

     for (int i = 0; i < table.getRowCount(); i++) 
      table.setValueAt(String.valueOf(i+1), i, 0); 

     table.setAutoCreateRowSorter(true); 
     JScrollPane scrollPane = new JScrollPane(table); 
     add(scrollPane, BorderLayout.CENTER); 
    } 

    private void newFilter(int numberOfRows) 
    { 
     System.out.println("Change the Filter to: " + numberOfRows); 

     RowFilter<TableModel, Integer> filter = new RowFilter<TableModel, Integer>() 
     { 
      @Override 
      public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry) 
      { 
       int modelRow = entry.getIdentifier(); 
       int viewRow = table.convertRowIndexToView(modelRow); 

       System.out.println("m" + modelRow + " : v" + viewRow); 

       return viewRow < numberOfRows; 
      } 

     }; 

     ((TableRowSorter) table.getRowSorter()).setRowFilter(filter); 
    } 

    private static void createAndShowGUI() 
    { 
     JPanel panel = new JPanel(); 

     JFrame frame = new JFrame("FilterSSCCE"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.add(new FilterSSCCE()); 
     frame.setLocationByPlatform(true); 
     frame.pack(); 
     frame.setVisible(true); 
    } 

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

Любая идея, как создать фильтр строк для отображения первых «N» строки?

О да, еще один расстраивающий пункт. Если вы раскомментируете две строки System.out .. в методе actionPeformed(), когда вы выберете 1 из поля со списком, вы заметите, что в обоих случаях индекс модели 4 преобразуется в индекс 4, и эти два выхода зажаты вокруг неверная модель для просмотра конверсий ???

Edit:

основе MadProgrammers внушения я попробовал:

//((TableRowSorter) table.getRowSorter()).setRowFilter(filter); 
TableRowSorter sorter = new TableRowSorter(); 
table.setRowSorter(sorter); 
sorter.setRowFilter(filter); 
sorter.sort(); 

Теперь я ничего не получаю в таблице.

+0

«Guess», что это «несколько раз», работающих с предварительно очищенным набором данных. .. – MadProgrammer

+0

Я закончил создание нового 'TableRowSorter', применив это к' JTable', задав 'rowFilter' этому сортировщику, а затем вызвал' sort' на 'TableRowSorter' ...: P – MadProgrammer

+0

@MadProgrammer,'. ... предварительно отфильтрованный набор данных «да», и я думаю, вопрос в том, почему? Я думаю, что большинство фильтров основаны на фактических данных в TableModel, так что это не проблема, поскольку отображение индексов будет переделано после фильтрации? «Я закончил создание ...» - не работал для меня. Думаю, я сделал что-то еще неправильно? – camickr

ответ

3

Используя советы от @MadProgrammer, я придумал следующее решение.

не только нужен RowSorter замены, также необходимо держать ключи сортировки поэтому метод сортировки() сбрасывает таблицу обратно в его текущем состоянии сортировки:

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

public class FilterSSCCE extends JPanel 
{ 
    private JTable table; 

    public FilterSSCCE() 
    { 
     setLayout(new BorderLayout()); 

     JComboBox<Integer> comboBox = new JComboBox<Integer>(); 
     comboBox.addItem(new Integer(1)); 
     comboBox.addItem(new Integer(2)); 
     comboBox.addItem(new Integer(3)); 
     comboBox.addItem(new Integer(4)); 
     comboBox.addItem(new Integer(5)); 
     comboBox.setSelectedIndex(4); 

     comboBox.addActionListener(new ActionListener() 
     { 
      @Override 
      public void actionPerformed(ActionEvent e) 
      { 
       Integer value = (Integer)comboBox.getSelectedItem(); 
       newFilter(value); 
      } 
     }); 
     add(comboBox, BorderLayout.NORTH); 

     table = new JTable(5, 1); 

     for (int i = 0; i < table.getRowCount(); i++) 
      table.setValueAt(String.valueOf(i+1), i, 0); 

     table.setAutoCreateRowSorter(true); 
     JScrollPane scrollPane = new JScrollPane(table); 
     add(scrollPane, BorderLayout.CENTER); 
    } 

    private void newFilter(int numberOfRows) 
    { 
     RowFilter<TableModel, Integer> filter = new RowFilter<TableModel, Integer>() 
     { 
      @Override 
      public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry) 
      { 
       int modelRow = entry.getIdentifier(); 
       int viewRow = table.convertRowIndexToView(modelRow); 

       return viewRow < numberOfRows; 
      } 

     }; 

     TableRowSorter oldSorter = (TableRowSorter)table.getRowSorter(); 
     TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(table.getModel()); 
     table.setRowSorter(sorter); 
     sorter.setRowFilter(filter); 
     sorter.setSortKeys(oldSorter.getSortKeys()); 
     sorter.sort(); 
    } 

    private static void createAndShowGUI() 
    { 
     JPanel panel = new JPanel(); 

     JFrame frame = new JFrame("FilterSSCCE"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.add(new FilterSSCCE()); 
     frame.setLocationByPlatform(true); 
     frame.pack(); 
     frame.setVisible(true); 
    } 

    public static void main(String[] args) 
    { 
     EventQueue.invokeLater(new Runnable() 
     { 
      public void run() 
      { 
       createAndShowGUI(); 
      } 
     }); 
    } 
} 
+0

Я предпочитаю ваше решение над своим «взломанным, взломанным» ... – MadProgrammer

+0

спасибо за это, очень интересно, [пока еще я не был уверен, что никогда не нужно что-то перезагружать] (http://stackoverflow.com/a/7132990/714968), потому что эта логика работает вместе с фильтром и сортировщиком (начиная с фильтрации, затем используется сортировщик) – mKorbel

+0

@MadProgrammer, даже это решение имеет потенциальная проблема в зависимости от точного требования. Если вы отобразите две строки (1, 2), а затем измените сортировку на нисходящую, вы увидите (2, 1). В действительности вы, вероятно, захотите увидеть (5, 4), что означает, что даже при изменении порядка сортировки нам нужно будет сбросить RowSorter и фильтр. Все усложняется. – camickr

5

Итак, после того, как некоторые серьезные тестирование и отладка, я скопировал код DefaultRowSorter и TableRowSorter в ваш FilterSSCCE и добавил некоторые результаты мониторинга в modelToView поле, который используется для отображения DefaultRowSorter#convertRowIndexToView между модельным и просмотреть indicies ...

Change the Filter to: 1 
createModelToView = [0, 0, 0, 0, 0] 
m0 : v0 
m1 : v0 
m2 : v0 
m3 : v0 
m4 : v0 
initializeFilteredMapping.1 = [0, 1, 2, 3, 4] 
initializeFilteredMapping.2 = [0, 1, 2, 3, 4] 
Change the Filter to: 5 
m0 : v0 
m1 : v1 
m2 : v2 
m3 : v3 
m4 : v4 
initializeFilteredMapping.1 = [0, 1, 2, 3, 4] 
initializeFilteredMapping.2 = [0, 1, 2, 3, 4] 
Change the Filter to: 1 
m0 : v0 
m1 : v1 
m2 : v2 
m3 : v3 
m4 : v4 
initializeFilteredMapping.1 = [0, -1, -1, -1, -1] 
initializeFilteredMapping.2 = [0, -1, -1, -1, -1] 
Change the Filter to: 2 
m0 : v0 
m1 : v-1 
m2 : v-1 
m3 : v-1 
m4 : v-1 
initializeFilteredMapping.1 = [0, 1, 2, 3, 4] 
initializeFilteredMapping.2 = [0, 1, 2, 3, 4] 

Интересная часть здесь находится в конце, между Change the Filter to: 1 и Change the Filter to: 2. Вы можете видеть, что initializeFilteredMapping установил индексы модели, которые находятся за пределами диапазона, до -1, но когда мы переходим на Change the Filter to: 2, те же самые индексы все еще установлены, изменение фильтра НЕ Сбрасывает их.

Это, кажется, выбор дизайна, чтобы сохранить таблицу отзывчивым, и они, вероятно, никогда не думал, что кто-то может попытаться получить доступ к вид внутри фильтра, как вы, предполагают, чтобы использовать данные модели ...

Как обойти это ...?

Вы можете создать «прокси» TableModel, но это исключает возможность сортировки таблицы.

Вы мог бы написать «проксите» TableModel который «знал» о отсортированноге состоянии JTable (вероятно, через RowSorter) и которые могут действовать в качестве фильтра, чтобы определить количество видимых строк, но это проникающее в мутную воду как модель начинает выходить в мир зрения ...

Другой выбор должен был бы изменить способ, которым setFilter работает метод и сбросить modelToView и viewToModel переменные, но они private, как они должны быть , хорошо, мы могли бы использовать методы createModelToView, createViewToModel и setModelToViewFromViewToModel, доступные в DefaultRowSorter ... но они private ...

Казалось бы, любой полезный метод, который касается серьезных изменений этих переменных, - private ... рассказ о моей жизни ... (получите факелы и вилы мы собираемся на Dev-охоты)

Следующая выбор, писать ВЕСЬ себя ... какая замечательная идея, ожидают, что идет вразрез с основными принципами OO ...

А «обойти «(и я использую этот термин очень, очень легко), было бы использовать отражение и просто называть методы, в которых мы нуждаемся ...

public class TestRowSorter<M extends TableModel> extends TableRowSorter<M> { 

    public TestRowSorter() { 
    } 

    public TestRowSorter(M model) { 
     super(model); 
    } 

    public Method findMethod(String name, Class... lstTypes) { 

     return findMethod(getClass(), name, lstTypes); 

    } 

    public Method findMethod(Class parent, String name, Class... lstTypes) { 

     Method method = null; 
     try { 
      method = parent.getDeclaredMethod(name, lstTypes); 
     } catch (NoSuchMethodException noSuchMethodException) { 
      try { 
       method = parent.getMethod(name, lstTypes); 
      } catch (NoSuchMethodException nsm) { 
       if (parent.getSuperclass() != null) { 
        method = findMethod(parent.getSuperclass(), name, lstTypes); 
       } 
      } 
     } 

     return method; 

    } 

    @Override 
    public void setRowFilter(RowFilter<? super M, ? super Integer> filter) { 

     try { 
      Method method = findMethod("createModelToView", int.class); 
      method.setAccessible(true); 
      method.invoke(this, getModelWrapper().getRowCount()); 

      method = findMethod("createViewToModel", int.class); 
      method.setAccessible(true); 
      method.invoke(this, getModelWrapper().getRowCount()); 

      method = findMethod("setModelToViewFromViewToModel", boolean.class); 
      method.setAccessible(true); 
      method.invoke(this, true); 
     } catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException exp) { 
      exp.printStackTrace(); 
     } 

     super.setRowFilter(filter); 
    } 

} 

Теперь, я уверен, что вы знаете, как я, это ужасная, ужасная идея, которая может сломаться в любой момент. Это, вероятно, также очень и очень неэффективно, так как вы каждый раз сбрасываете индексы на двунаправленный поиск.

Итак, ответ, не обращайтесь к виду с фильтра.

Вообще говоря, я, как правило, заменить RowSorter, когда я когда-либо заменить RowFilter как это позволяет избежать такого рода проблем: P

+1

Спасибо за полный анализ (+1). 'не получить доступ к виду из фильтра' - Казалось, что простое требование просто ограничить количество строк, отображаемых в представлении. Но если вы думаете об этом, действительно ли это важно, если будут отображаться все строки? Может быть, более простым решением было бы выделить фон первых «n» строк по-другому? – camickr