Как работает Jtable RowFilter?
Я пытаюсь создать фильтр строк для JTable, чтобы ограничить количество строк, отображаемых в таблице.
код 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 " строк?
Ах да, еще один разочаровывающий момент. Если вы раскомментируете две системы.из.. строки в методе actionPeformed () при выборе 1 из поля со списком вы заметите, что в обоих случаях индекс модели 4 преобразуется в индекс просмотра 4, и эти два выхода зажаты вокруг неправильной модели для просмотра преобразований???
Edit:
основываясь на предложении MadProgrammers, я попытался:
//((TableRowSorter) table.getRowSorter()).setRowFilter(filter);
TableRowSorter sorter = new TableRowSorter();
table.setRowSorter( sorter );
sorter.setRowFilter( filter );
sorter.sort();
теперь я получаю в столе ничего нет.
2 ответов
Итак, после серьезного тестирования и отладки, я скопировал код DefaultRowSorter
и TableRowSorter
в своем FilterSSCCE
и добавил некоторый мониторинг вывода modelToView
поле, которое используется DefaultRowSorter#convertRowIndexToView
для отображения между моделью и просмотра показателей...
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
to...
казалось бы, почти любой полезный метод, который имеет дело с серьезной модификацией этих переменных, -private
...история моей жизни...(берите свои факелы и вилы, мы отправляемся на охоту)
следующий выбор, напишите все сами...какая замечательная идея, ожидайте, что это идет вразрез с основными принципами ОО...
"работа вокруг" (и я использую термин очень, очень легко), было бы используйте рефлексию и просто вызывайте методы, которые нам нужны...
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
используя советы от @MadProgrammer, я придумал следующее решение.
мало того, что нужно заменить RowSorter, вам также нужно сохранить ключи сортировки, чтобы метод sort () сбросил таблицу до ее текущего состояния сортировки:
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();
}
});
}
}