dataModelUpdate

时间:2017-05-10 03:11:43

标签: java swing jtable tablerowsorter

在数据模型更改后继续排序。

预期行为 我单击其中一个列标题进行排序,并且它应该在dataModel更改时继续对该列进行排序。也就是说,一旦您选择要排序的列,您就不必在每次更新基础数据模型后手动单击它以按顺序查看它。

当前行为: 似乎在数据模型更改后重置排序。也就是说,好像我没有点击任何列。数据模型的更新使它再次混乱。

我尝试了什么: 我写了一个小程序,它通过for循环模拟底层数据模型的变化,每2000毫秒。 由于原始数据从更新到更新都发生了很大变化,因此我选择使用DefaultTableModel.setDataVector()更改数据,而不是逐行更改,如果有大量行更改则看起来很笨重。 我使用了对我有意义的东西,即TableRowSorter.setSortsOnUpdates(),以及......更新后的排序x)。 我留下了注释掉代码,表明我尝试了不同的方法,一些在stackexchange上找到,但无论如何都无法使它工作。 我试着阅读api并发现这是一个可能的问题:

  

如果底层模型结构发生更改(调用modelStructureChanged方法),则会将以下内容重置为其默认值:按列排序的比较器,当前排序顺序以及每列是否可排序。默认排序顺序是自然的(与模型相同),默认情况下列可排序。

这可以在API文档中找到:TableRowSorter

我想有人可能会说我希望它重置为默认排序顺序,而是保留最后选择的顺序。当然必须有一种简单的方法来阻止它这样做吗?

Btw我是一个菜鸟,请在回答时记住这一点,我希望我在试图解释预期的行为时有意义。

我的代码适用:

package TestTable;

import java.awt.Color;
import java.awt.Dimension;
import java.util.Random;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;
import ui.Runner;

public class starter {
    public static void main(String[] args) {
        starter start = new starter();
        Vector<String> columNames = new Vector<>();
        columNames.add("index");
        columNames.add("years");
        columNames.add("weight");
        Vector<Vector<Object>> data = new Vector<Vector<Object>>();
        Vector<Object> dataInData = new Vector<>();
        Random dice = new Random();

        JTable table = new JTable(data, columNames);
        //table.setAutoCreateRowSorter(true);
        MyFrame frame = new MyFrame(table);
        DefaultTableModel model = (DefaultTableModel)table.getModel();
        TableRowSorter<DefaultTableModel> sorter = new TableRowSorter<>(model);
        //MyRowSorter<DefaultTableModel> sorter = new MyRowSorter<>();
        table.setRowSorter(sorter);
        //DefaultRowSorter sorter = (DefaultRowSorter)table.getRowSorter();
        //TableModelEvent ev = new TableModelEvent(model);
        sorter.setSortsOnUpdates(true);//<---------------<<<<<

        data.add(new Vector<Object>());
        data.add(new Vector<Object>());
        data.add(new Vector<Object>());

        for(int x = 0; x < 3; x++) {
            dataInData = data.get(x);
            for(int y = 0; y < 5; y++) {
                dataInData.add(dice.nextInt(10));
            }
        }

        for(int z = 0; z < 10; z++) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for(int x = 0; x < 3; x++) {
                dataInData = data.get(x);
                for(int y = 0; y < dataInData.size(); y++) {
                    dataInData.set(y, dice.nextInt(10));
                }
            }

            model.setDataVector(data, columNames);//<-------------------<<
            //model.fireTableDataChanged();
            //sorter.setSortsOnUpdates(true);
            //sorter.rowsUpdated(0, data.size() -1);
            model.fireTableRowsUpdated(0, model.getRowCount() -1 );
            //model.fireTableDataChanged();
            //model.fireTableChanged(ev);
            //model.fireTableStructureChanged();
            //sorter.rowsUpdated(0, data.size() );
            //sorter.sort();
        }
    }

    public static class MyFrame extends JFrame{
        JTable table;
        Dimension dim = new Dimension(300, 500);
        JScrollPane pane;
        Vector<String> columNames = new Vector<>();
        Vector<Vector<Object>> dataVector;
        Runner thread;
        int[] i = {0, 1, -1};

        public MyFrame(JTable table) {
            pane = new JScrollPane(table);
            table.setFillsViewportHeight(true);
            setSize(300, 500);
            setPreferredSize(dim);
            setMinimumSize(dim);
            setTitle("Failing autoupdate of JTable");
            setBackground(Color.BLACK);
            add(pane);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setVisible(true);
        }
    }
}

4 个答案:

答案 0 :(得分:2)

让我们暂时解决线程违规问题。

setDataVector上拨打DefaultTableModel会触发TableStructureChanged,这会迫使JTable放弃/重置rowSorter,相反,只需直接更新模型中的值,例如

for (int x = 0; x < 3; x++) {
    for (int y = 0; y < table.getColumnCount(); y++) {
        model.setValueAt(dice.nextInt(10), x, y);
    }
}

我使用Swing Timer对此进行了测试,以确保更新正确地在EDT中进行

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;

public class Main {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Main();
            }
        });
    }

    public Main() {
        Vector<String> columNames = new Vector<>();
        columNames.add("index");
        columNames.add("years");
        columNames.add("weight");
        Vector<Vector<Object>> data = new Vector<Vector<Object>>();
        Random dice = new Random();

        JTable table = new JTable(data, columNames);
        MyFrame frame = new MyFrame(table);
        DefaultTableModel model = (DefaultTableModel) table.getModel();
        TableRowSorter<DefaultTableModel> sorter = new TableRowSorter<>(model);
        table.setRowSorter(sorter);
        sorter.setSortsOnUpdates(true);//<---------------<<<<<

        for (int x = 0; x < 3; x++) {
            model.addRow(new Vector());
            for (int y = 0; y < table.getColumnCount(); y++) {
                model.setValueAt(dice.nextInt(10), x, y);
            }
        }

        Timer timer = new Timer(2000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                for (int x = 0; x < 3; x++) {
                    for (int y = 0; y < table.getColumnCount(); y++) {
                        model.setValueAt(dice.nextInt(10), x, y);
                    }
                }
            }
        });
        timer.start();
    }

    public static class MyFrame extends JFrame {

        JTable table;
        Dimension dim = new Dimension(300, 500);
        JScrollPane pane;
        Vector<String> columNames = new Vector<>();
        Vector<Vector<Object>> dataVector;
        int[] i = {0, 1, -1};

        public MyFrame(JTable table) {
            pane = new JScrollPane(table);
            table.setFillsViewportHeight(true);
            setSize(300, 500);
            setPreferredSize(dim);
            setMinimumSize(dim);
            setTitle("Failing autoupdate of JTable");
            setBackground(Color.BLACK);
            add(pane);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setVisible(true);
        }
    }
}
  

我选择使用DefaultTableModel.setDataVector()更改数据,而不是逐行更改数据,如果有大量行更改,这似乎很笨重

好的,这似乎是合理的。我似乎能够为此工作的唯一解决方案是替换RowSorter,但使用旧sortKeys

中的RowSorter

仅使用同一个RowSorter实例对我不起作用

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

public class Main {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Main();
            }
        });
    }

    Random dice = new Random();

    public Main() {
        Vector<String> columNames = new Vector<>();
        columNames.add("index");
        columNames.add("years");
        columNames.add("weight");
        Vector<Vector<Object>> data = new Vector<Vector<Object>>();

        JTable table = new JTable();
        makeModel(table, columNames);
        MyFrame frame = new MyFrame(table);

        Timer timer = new Timer(2000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                makeModel(table, columNames);
            }
        });
        timer.start();
    }

    protected void makeModel(JTable table, Vector columnNames) {
        DefaultTableModel model = new DefaultTableModel(columnNames, 0);

        for (int x = 0; x < 3; x++) {
            Vector row = new Vector();
            for (int y = 0; y < model.getColumnCount(); y++) {
                row.add(dice.nextInt(10));
            }
            model.addRow(row);
        }
        TableRowSorter<DefaultTableModel> sorter = new TableRowSorter<>(model);
        RowSorter<? extends TableModel> oldSorter = table.getRowSorter();
        if (oldSorter != null) {
            sorter.setSortKeys(oldSorter.getSortKeys());
        }

        table.setModel(model);
        table.setRowSorter(sorter);
    }

    public static class MyFrame extends JFrame {

        JTable table;
        Dimension dim = new Dimension(300, 500);
        JScrollPane pane;
        Vector<String> columNames = new Vector<>();
        Vector<Vector<Object>> dataVector;
        int[] i = {0, 1, -1};

        public MyFrame(JTable table) {
            pane = new JScrollPane(table);
            table.setFillsViewportHeight(true);
            setSize(300, 500);
            setPreferredSize(dim);
            setMinimumSize(dim);
            setTitle("Failing autoupdate of JTable");
            setBackground(Color.BLACK);
            add(pane);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setVisible(true);
        }
    }
}

请注意,此类更新也会删除列/行选择

  

我选择使用DefaultTableModel.setDataVector()更改数据,而不是逐行更改数据,如果有大量行更改,这似乎很笨重

另一个选择是不使用DefaultTableModel,而是使用AbstractTableModel实现自己的,在那里对基础模型数据进行“批量”更新,然后触发tableDataChanged事件。在完成批量更新之前,您必须小心确保不要修改JTable可能要求的数据

答案 1 :(得分:0)

设置新的首选索引时,是否要保留排序? 你只有三个列标题(有限),为什么不设置关于列标题的优先级? 当您单击一个标题时,将其优先级更改为第一个,将其他标识符减少一个。 并再次计算总排序以显示它们。

答案 2 :(得分:0)

  

我选择使用DefaultTableModel.setDataVector()更改数据,而不是逐行更改数据,如果有很多行更改,这似乎很笨重。

如果所有数据都在变化,似乎是合理的,因为您只会生成一个事件来重新绘制整个表而不是每一行都有一个事件。

但是,如果您只更改几个随机行的数据,那么我只会更新单个行,然后您就不会遇到排序问题。

对于第一种方法,请尝试以下方法:

JTable table = new JTable(data, columNames); 
table.setAutoCreateColumnsFromModel(false);

这会阻止在调用TableColumnModel时重新创建setDataVector(...),这会导致生成TableStructureChanged事件,从而导致您丢失RowSorter。 (或者你仍然失去了RowSorter,我从来没有尝试过,但我确实知道你用这种方法失去了自定义渲染器/编辑器。)

//model.fireTableRowsUpdated(0, model.getRowCount() -1 );

这是永远不需要的。 TableModel负责在数据更改时调用正确的事件。

答案 3 :(得分:0)

我遇到了同样的问题,在研究了JDK源代码之后,恢复排序的最简单方法似乎是使用以下代码对JTable进行子类化(但这不能解决另一个问题-选择也正在重置并且需要恢复)。

RowSorter<? extends TableModel> lastRowSorter;
List<? extends RowSorter.SortKey> lastSortKeys;

@Override
public void tableChanged(TableModelEvent e) {
    lastRowSorter = getRowSorter();
    lastSortKeys = (lastRowSorter == null) ? null : lastRowSorter.getSortKeys();

    super.tableChanged(e);
}

@Override
public void createDefaultColumnsFromModel() {
    super.createDefaultColumnsFromModel();
    if( lastSortKeys != null ) {
        setRowSorter(lastRowSorter);
        lastRowSorter.setSortKeys(lastSortKeys);
    }
}