Полоса прокрутки JScrollPane не появляется до тех пор, пока не будет вызван rowSorter.toggleSortOrder()

Я заметил, что когда у меня есть JTable с TableRowSorter, содержащимся в JScrollPane, вертикальная полоса прокрутки не появляется до тех пор, пока я не создам SortKeys для сортировщика (что делается путем вызова toggleSortOrder() для одного из столбцов).

Мой вопрос действительно почему? Какое отношение SortKeys имеет к вертикальной полосе прокрутки?

Обновление: добавлен SSCCE, который открывает JFrame с JTable внутри JScrollPane, который находится в контейнере вместе с кнопкой «Заполнить». Когда таблица первоначально окрашена, в ней нет данных и, следовательно, нет необходимости в полосе прокрутки. После того, как я заполнил его 20 строками, нужна полоса прокрутки, но она не появляется.

Отобразить полосу прокрутки можно двумя способами:

  • Щелкните любую из ячеек заголовка, чтобы выполнить сортировку.
  • Удалите закомментированный вызов toggleSortOrder() в методе 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);
    }

}

person Robert White    schedule 24.07.2014    source источник
comment
поделитесь своим примером кода для четкой картины, потому что проблема скрыта где-то в самом вашем коде.   -  person Braj    schedule 24.07.2014
comment
My question is really why? у вас ошибка в коде. RowSorter не имеет ничего общего с полосой прокрутки. Опубликуйте свой SSCCE, демонстрирующий проблему.   -  person camickr    schedule 24.07.2014


Ответы (1)


Ну, на самом деле это не SSCCE, потому что вы используете пользовательскую TableModel. Если бы вы создали правильный SSCCE, вы бы использовали DefaultTableModel, чтобы тестировать свой код со стандартными классами JDK. Если бы вы сделали это, вы бы заметили, что код будет работать.

Итак, вашим следующим шагом было бы попробовать код с вашей собственной TableModel, и вы бы заметили, что код не работает.

Итак, ваш вопрос на форуме будет заключаться в том, почему код не работает с моей пользовательской TableModel? Смысл SSCCE состоит в том, чтобы выполнить базовую отладку, чтобы изолировать, где происходит ошибка, чтобы у нас была информация для работы. В вашем первоначальном вопросе мы понятия не имели, где вы используете пользовательские классы.

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

  fireTableRowsInserted(0, names.size()-1);

Нет необходимости в table.repaint() ни в одном из ваших кодов.

person camickr    schedule 25.07.2014
comment
Согласно ссылке, которую вы предоставили ранее, SSCCE должен убедиться, что пример правильный. Либо пример компилируется чисто, либо вызывает точное сообщение об ошибке, которое требует решения. Было бы бессмысленно включать пример решения проблемы, для решения которой мне понадобился ваш опыт. Остается вопрос: какое отношение SortKeys имеет к вертикальной полосе прокрутки? Методы sort() и sortExistingData() вызывают fireRowSorterChanged(), а не fireTableRowsInserted(), который лучше использовать. Я делаю вывод, что JScrollPane является TableModelListener и RowSorterListener. - person Robert White; 25.07.2014
comment
It would have been pointless to include an example that solved the problem, - это НЕ то, что я сказал. Я сказал, что вам нужно научиться выполнять базовую отладку ДО того, как вы зададите вопрос. Затем вы указываете, что вы наблюдали, вместо широко открытого вопроса, спрашивая, что может быть не так. Таким образом, ваш вопрос должен был звучать примерно так: When I use my custom TableModel the code doesn't work и вы включаете код со своей собственной TableModel. Итак, теперь мы тратим меньше времени на просмотр всего остального кода и концентрируемся на TableModel. - person camickr; 26.07.2014
comment
which was the better method to have used - вы используете подходящий метод, который подходит к ситуации. В коде, который вы разместили здесь, вы добавляете новые строки данных в модель. Когда вы используете RowSorter, вы сортируете существующие строки. - person camickr; 26.07.2014