Полоса прокрутки JScrollPane не появляется до тех пор, пока не будет вызвана rowSorter.toggleSortOrder()
Я заметил, что когда у меня есть JTable с TableRowSorter, содержащимся в JScrollPane, вертикальная полоса прокрутки появляется только после того, как я создал SortKeys для сортировщика (что делается вызовом toggleSortOrder() для одного из столбцов).
Мой вопрос действительно почему? Какое отношение SortKeys имеет к вертикальной полосе прокрутки?
Обновление: добавлен SSCCE, который открывает JFrame с JTable внутри JScrollPane, который находится в контейнере вместе с кнопкой "Заполнить". Когда таблица первоначально нарисована, нет данных и, следовательно, нет необходимости в полосе прокрутки. После того, как я заполнил его 20 строками, появляется необходимость в полосе прокрутки, но ни одна не появляется.
Есть два способа отображения полосы прокрутки:
- Нажмите на любую из ячеек заголовка, чтобы вызвать сортировку.
Удалите закомментированный вызов toggleSortOrder() в методе Container refresh().
// table.getRowSorter().toggleSortOrder(0);
toggleSortOrder()
звонки setSortKeys()
звонки sort()
звонки fireRowSorterChanged()
и в конце концов что-то ловит событие и добавляет полосу прокрутки.
package test;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.WindowConstants;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;
@SuppressWarnings("serial")
public class TestFrame extends JFrame
{
class MyTableModel extends AbstractTableModel
{
public List<String> names = new ArrayList<String>();
public int getRowCount ()
{
return names.size();
}
public int getColumnCount ()
{
return 2;
}
public String getColumnName (int columnIndex)
{
return columnIndex > 0 ? "Name" : "Number";
}
public Class<?> getColumnClass (int columnIndex)
{
return String.class;
}
public Object getValueAt (int row, int col)
{
return row < names.size() ? col == 0 ? Integer.toString(row) : names.get(row) : "";
}
public void refresh (List<String> names)
{
this.names = names;
}
}
class MyContainer extends java.awt.Container implements ActionListener
{
JTable table;
MyTableModel model = new MyTableModel();
private TableRowSorter<MyTableModel> sorter;
public MyContainer()
{
}
public void init ()
{
sorter = new TableRowSorter<MyTableModel>(model);
table = new JTable(model);
table.setBorder(BorderFactory.createEmptyBorder());
table.setRowHeight(35);
table.getTableHeader().setPreferredSize(new Dimension(200, 35));
table.setRowSorter(sorter);
table.setPreferredScrollableViewportSize(new Dimension(200, 70));
table.setFillsViewportHeight(true);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
//Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setBounds(10, 10, 200, 210);
//Add the scroll pane to this panel.
add(scrollPane);
JButton btn = new JButton("Populate");
btn.setActionCommand("populate");
btn.addActionListener(this);
btn.setBounds(10, 220, 200, 35);
add(btn);
}
public void refresh (List<String> rows)
{
model.refresh(rows);
try
{
// Notify sorter that model data (possibly number of rows) has changed.
// Without this call, the sorter assumes the number of rows is the same.
table.getRowSorter().allRowsChanged();
// Do we really want to toggle the sort order every time we refresh?
// User can toggle the sort order himself by clicking on the
// appropriate header cell.
List<?> keys = table.getRowSorter().getSortKeys();
if (null == keys || keys.isEmpty())
{
// table.getRowSorter().toggleSortOrder(0);
}
} catch (Exception e)
{
e.printStackTrace();
}
table.repaint();
}
public void actionPerformed(ActionEvent e)
{
if ("populate".equals(e.getActionCommand()))
{
List<String> rows = new ArrayList<String>();
for (int ii = 0; ii < 20; ii++)
{
rows.add(String.format("%02d", new Integer(ii)));
}
refresh(rows);
}
}
MyTableModel getModel ()
{
return model;
}
}
public static void main (String args[])
{
new TestFrame();
}
MyContainer myContainer = new MyContainer();
TestFrame()
{
myContainer.init();
myContainer.table.getSelectionModel().clearSelection();
add(myContainer);
this.setSize(240, 310);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//pack();
setVisible(true);
}
}
1 ответ
Ну, на самом деле это не SSCCE, потому что вы используете пользовательскую TableModel. Если бы вы создали правильный SSCCE, вы бы использовали DefaultTableModel, чтобы тестировать свой код со стандартными классами JDK. Если бы вы сделали это, вы бы заметили, что код будет работать.
Итак, следующим шагом будет попытка кода с вашей пользовательской TableModel, и вы заметите, что код не работает.
Тогда ваш вопрос на форуме: почему код не работает с моей пользовательской TableModel? Смысл SSCCE состоит в том, чтобы выполнить базовую отладку, чтобы изолировать, где происходит ошибка, чтобы у нас была информация для работы. В вашем первоначальном вопросе мы не знали, где вы используете пользовательские классы.
В любом случае, проблема в том, что ваш пользовательский TableModel не уведомляет таблицу, когда вносятся изменения в данные. В вашем refresh(...)
Метод, вам нужно добавить следующее после сброса списка, содержащего данные:
fireTableRowsInserted(0, names.size()-1);
Там нет необходимости table.repaint()
в любом вашем коде.