【问题标题】:Can't Add TableRowSorter to JTable Produced By SwingWorker无法将 TableRowSorter 添加到 SwingWorker 生成的 JTable
【发布时间】:2017-08-26 22:14:36
【问题描述】:

感谢Hovercraft Full Of Eels 注意到我的问题充满了无法解决的混乱代码这一事实。从那时起,我创建了一个“最小”测试程序来显示问题:

问题

我想要做的是有一个 GUI,它显示一个包含员工信息的表格,并且还允许用户通过在 gui 顶部的 jtextfield 中键入来对所述表格进行实时搜索。

所以我目前有一个 java 类,它创建一个表并用员工信息填充该表。此信息通过由SwingWorker 处理的SQL 语句放入表中。 GUI 工作得非常好,直到我尝试将 TableRowSorter 添加到包含信息的表中,这会产生错误:

Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Invalid range
    at javax.swing.DefaultRowSorter.checkAgainstModel(Unknown Source)
    at javax.swing.DefaultRowSorter.rowsInserted(Unknown Source)
    at javax.swing.JTable.notifySorter(Unknown Source)
    at javax.swing.JTable.sortedTableChanged(Unknown Source)
    at javax.swing.JTable.tableChanged(Unknown Source)
    at javax.swing.table.AbstractTableModel.fireTableChanged(Unknown Source)
    at javax.swing.table.AbstractTableModel.fireTableRowsInserted(Unknown Source)
    at tesPackage.WorkerTest$JDBCModel$JDBCWorker.process(WorkerTest.java:276)

基于这个example,这里有一个

工作示例

package tesPackage;

import java.awt.BorderLayout;
import java.util.concurrent.ThreadLocalRandom;
import java.awt.EventQueue;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.RowFilter;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

/**
* @see https://stackoverflow.com/a/34742409/230513
* @see https://stackoverflow.com/a/24762078/230513
*/
public class WorkerTest {

private static final int N = 25;
private static final String URL = "jdbc:h2:mem:test";
private static final Random r = new Random();

private JPanel contentPane;
private static JTable tblSchedule;
private JScrollPane scrollSchedTable;
private JDBCModel model;
private JPanel panel;
private JTextField textField;

private static TableRowSorter<TableModel> rowSorter;

private void display() {

    createTestDatabase(N);  //Create the Test Database

    //Create the GUI
    JFrame f = new JFrame("WorkerTest");
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setBounds(100, 100, 780, 450);
    contentPane = new JPanel();
    contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    f.setContentPane(contentPane);
    contentPane.setLayout(new BorderLayout(0, 0));
    {
        scrollSchedTable = new JScrollPane();
        scrollSchedTable.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        contentPane.add(scrollSchedTable);
        {
            tblSchedule = new JTable();
            tblSchedule.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            tblSchedule.setShowHorizontalLines(true);
            tblSchedule.setShowVerticalLines(true);
            tblSchedule.setFillsViewportHeight(true);
            model = new JDBCModel(getConnection(), "select * from city");
            tblSchedule.setModel(model);
            rowSorter = new TableRowSorter<TableModel>(model);  //IF UNCOMMENT THESE 2 LINES GUI WILL RUN
            tblSchedule.setRowSorter(rowSorter);                //BUT IF TYPE IN SEARCH BOX THERE IS AN ERROR
            scrollSchedTable.setViewportView(tblSchedule);
        }
        {
            panel = new JPanel();
            contentPane.add(panel, BorderLayout.NORTH);
            panel.setLayout(new BorderLayout(0, 0));
            {
                textField = new JTextField();
                panel.add(textField, BorderLayout.EAST);
                textField.setColumns(10);
            }
            {
                panel.add(model.jpb, BorderLayout.CENTER);
            }
            {
                textField.getDocument().addDocumentListener(new DocumentListener(){

                    @Override
                    public void insertUpdate(DocumentEvent e) {
                        String text = textField.getText().trim();
                        if (text.trim().length() == 0) {
                            rowSorter.setRowFilter(null);
                        } else {
                            rowSorter.setRowFilter(RowFilter.regexFilter("(?i)" + text));
                        }
                    }

                    @Override
                    public void removeUpdate(DocumentEvent e) {
                        String text = textField.getText();
                        if (text.trim().length() == 0) {
                            rowSorter.setRowFilter(null);
                        } else {
                            rowSorter.setRowFilter(RowFilter.regexFilter("(?i)" + text));
                        }
                    }

                    @Override
                    public void changedUpdate(DocumentEvent e) {
                        String text = textField.getText();
                        if (text.trim().length() == 0) {
                            rowSorter.setRowFilter(null);
                        } else {
                            rowSorter.setRowFilter(RowFilter.regexFilter("(?i)" + text));
                        }
                    }

                });
            }
        }
    }
    f.setVisible(true);
}

private static class Row {
    String lname;
    String fname;
    String monTime;
    String tueTime;
    String wedTime;
    String thuTime;
    String friTime;
    String satTime;
    String sunTime;
    String totalTime;
}

private static class JDBCModel extends AbstractTableModel {

    private final JProgressBar jpb = new JProgressBar();
    private static final long serialVersionUID = 1L;
    private final List<Row> data = new ArrayList<>();
    private ResultSet rs = null;
    private ResultSetMetaData meta;

    public JDBCModel(Connection conn, String query) {
        try {
            Statement s = conn.createStatement();
            rs = s.executeQuery(query);
            meta = rs.getMetaData();
            JDBCWorker worker = new JDBCWorker();
            jpb.setIndeterminate(true);
            worker.execute();

        } catch (SQLException e) {
            e.printStackTrace(System.err);
        }
    }

    @Override
    public int getRowCount() {
        return data.size();
    }

    @Override
    public int getColumnCount() {
        return 10;
    }



    @Override
    public Object getValueAt(int rowIndex, int colIndex) {
        Row row = data.get(rowIndex);
        switch (colIndex) {
            case 0:
                return row.lname;
            case 1:
                return row.fname;
            case 2:
                return row.monTime;
            case 3:
                return row.tueTime;
            case 4:
                return row.wedTime;
            case 5:
                return row.thuTime;
            case 6:
                return row.friTime;
            case 7:
                return row.satTime;
            case 8:
                return row.sunTime;
            case 9:
                return row.totalTime;
        }
        return null;
    }

    @Override
    public String getColumnName(int colIndex) {
        String name = null;
        switch(colIndex){
        case 0: name = "Last Name";
            return name;
        case 1: name = "First Name";
            return name;
        case 2: name = "Monday";
            return name;
        case 3: name = "Tuesday";
            return name;
        case 4: name = "Wednesday";
            return name;
        case 5: name = "Thursday";
            return name;
        case 6: name = "Friday";
            return name;
        case 7: name = "Saturday";
            return name;
        case 8: name = "Sunday";
            return name;
        case 9: name = "Total";
            return name;
        }
        return name;
    }

    private class JDBCWorker extends SwingWorker<List<Row>, Row> {

        @Override
        protected List<Row> doInBackground() {
            try {
                while (rs.next()) {
                    Row r = new Row();
                    Integer total = 0;
                    r.lname = rs.getString(2);
                    r.fname = rs.getString(1);
                    r.monTime = String.valueOf(rs.getInt(3) + " - " + rs.getInt(4));
                    total += rs.getInt(4) - rs.getInt(3);
                    r.tueTime = String.valueOf(rs.getInt(5) + " - " + rs.getInt(6));
                    total += rs.getInt(6) - rs.getInt(5);
                    r.wedTime = String.valueOf(rs.getInt(7) + " - " + rs.getInt(8));
                    total += rs.getInt(8) - rs.getInt(7);
                    r.thuTime = String.valueOf(rs.getInt(9) + " - " + rs.getInt(10));
                    total += rs.getInt(10) - rs.getInt(9);
                    r.friTime = String.valueOf(rs.getInt(11) + " - " + rs.getInt(12));
                    total += rs.getInt(12) - rs.getInt(11);
                    r.satTime = String.valueOf(rs.getInt(13) + " - " + rs.getInt(14));
                    total += rs.getInt(14) - rs.getInt(13);
                    r.sunTime = String.valueOf(rs.getInt(15) + " - " + rs.getInt(16));
                    total += rs.getInt(16) - rs.getInt(15);
                    r.totalTime = String.valueOf(total);
                    publish(r);
                }
            } catch (SQLException e) {
                e.printStackTrace(System.err);
            }
            return data;
        }

        @Override
        protected void process(List<Row> chunks) {
            int n = getRowCount() + 1;
            System.out.println("Row Count = " + n);
            System.out.println("Chunks Size = " + chunks.size());
            for (Row row : chunks) {
                data.add(row);
            }
            fireTableRowsInserted(n, n + chunks.size());
        }

        @Override
        protected void done(){
            jpb.setIndeterminate(false);
            jpb.setValue(100);
        }
    }
}

//Creates the Test DB with test names and working hours
private static void createTestDatabase(int n) {
    Connection conn = getConnection();
    try {
        Statement st = conn.createStatement();
        st.execute("create table city(fname varchar(10), lname varchar(10), mons int(2), mone int(2), tues int(2), tuee int(2), weds int(2), wede int(2), thus int(2), thue int(2), fris int(2), frie int(2), sats int(2), sate int(2), suns int(2), sune int(2))");
        Random gen = new Random();
        PreparedStatement ps = conn.prepareStatement(
            "insert into city values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
        for (int i = 0; i < n; i++) {
            ps.setString(1, "First" + i);
            ps.setString(2, "Last" + i);
            ps.setInt(3, ThreadLocalRandom.current().nextInt(6, 12));
            ps.setInt(4, ThreadLocalRandom.current().nextInt(13, 22));
            ps.setInt(5, ThreadLocalRandom.current().nextInt(6, 12));
            ps.setInt(6, ThreadLocalRandom.current().nextInt(13, 22));
            ps.setInt(7, ThreadLocalRandom.current().nextInt(6, 12));
            ps.setInt(8, ThreadLocalRandom.current().nextInt(13, 22));
            ps.setInt(9, ThreadLocalRandom.current().nextInt(6, 12));
            ps.setInt(10, ThreadLocalRandom.current().nextInt(13, 22));
            ps.setInt(11, ThreadLocalRandom.current().nextInt(6, 12));
            ps.setInt(12, ThreadLocalRandom.current().nextInt(13, 22));
            ps.setInt(13, ThreadLocalRandom.current().nextInt(6, 12));
            ps.setInt(14, ThreadLocalRandom.current().nextInt(13, 22));
            ps.setInt(15, ThreadLocalRandom.current().nextInt(6, 12));
            ps.setInt(16, ThreadLocalRandom.current().nextInt(13, 22));
            ps.execute();
        }
    } catch (SQLException ex) {
        ex.printStackTrace(System.err);
    }
}

private static Connection getConnection() {
    try {
        return DriverManager.getConnection(URL, "", "");
    } catch (SQLException e) {
        e.printStackTrace(System.err);
    }
    return null;
}

public static void main(String[] args) {
    EventQueue.invokeLater(new WorkerTest()::display);
}
}

编辑(仅 5 分钟后)

所以我开始在 Eclipse 中使用调试功能,当我单步执行程序时,我发现一些名为 EDT.pumpOneEventForFilters(int) 的事件有一个名为 arg0(它是唯一的参数)的变量,其值为 -1 .一旦进入这个事件,就会抛出异常。我不确定这是否是有用的信息,但我想我会把它扔在那里以防万一。

【问题讨论】:

  • 1) 哪一行是GuiFullSchedule.java:299? 2) 为什么不在您的问题代码中发布有效的minimal reproducible example
  • @HovercraftFullOfEels 我已经编辑了我的帖子以包含我的问题的完整工作示例程序。谢谢。
  • 别忘了引用original code,如required
  • @trashgod 说了什么
  • 您可以在process() 中尝试fireTableDataChanged()。为什么ThreadLocalRandom 有这么多实例?一个就够了。

标签: java sql swing search tablemodel


【解决方案1】:

exampleprocess()的实现中通知了监听JTable的新行:

fireTableRowsInserted(n, n + chunks.size());

因为表格在排序和过滤时需要考虑所有行,所以模型应该通知“listeners that all cell values in the table's rows may have changed。”

fireTableDataChanged();

顺便说一句,ThreadLocalRandom 的单个实例在 createTestDatabase() 中就足够了。

经过测试的代码:

import java.awt.BorderLayout;
import java.util.concurrent.ThreadLocalRandom;
import java.awt.EventQueue;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.RowFilter;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

/**
 * @see https://stackoverflow.com/q/43161033/230513
 * @see https://stackoverflow.com/a/34742409/230513
 * @see https://stackoverflow.com/a/24762078/230513
 */
public class WorkerTest {

    private static final int N = 256;
    private static final String URL = "jdbc:h2:mem:test";
    private static final Random r = new Random();

    private JPanel contentPane;
    private static JTable tblSchedule;
    private JScrollPane scrollSchedTable;
    private JDBCModel model;
    private JPanel panel;
    private JTextField textField;

    private static TableRowSorter<TableModel> rowSorter;

    private void display() {

        createTestDatabase(N);  //Create the Test Database

        //Create the GUI
        JFrame f = new JFrame("WorkerTest");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setBounds(100, 100, 780, 450);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        f.setContentPane(contentPane);
        contentPane.setLayout(new BorderLayout(0, 0));

        scrollSchedTable = new JScrollPane();
        scrollSchedTable.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        contentPane.add(scrollSchedTable);

        tblSchedule = new JTable();
        tblSchedule.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        tblSchedule.setShowHorizontalLines(true);
        tblSchedule.setShowVerticalLines(true);
        tblSchedule.setFillsViewportHeight(true);
        model = new JDBCModel(getConnection(), "select * from city");
        tblSchedule.setModel(model);
        rowSorter = new TableRowSorter<TableModel>(model);
        tblSchedule.setRowSorter(rowSorter);
        scrollSchedTable.setViewportView(tblSchedule);

        panel = new JPanel();
        contentPane.add(panel, BorderLayout.NORTH);
        panel.setLayout(new BorderLayout(0, 0));

        textField = new JTextField();
        panel.add(textField, BorderLayout.EAST);
        textField.setColumns(10);

        panel.add(model.jpb, BorderLayout.CENTER);

        textField.getDocument().addDocumentListener(new DocumentListener() {

            @Override
            public void insertUpdate(DocumentEvent e) {
                String text = textField.getText().trim();
                if (text.trim().length() == 0) {
                    rowSorter.setRowFilter(null);
                } else {
                    rowSorter.setRowFilter(RowFilter.regexFilter("(?i)" + text));
                }
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                String text = textField.getText();
                if (text.trim().length() == 0) {
                    rowSorter.setRowFilter(null);
                } else {
                    rowSorter.setRowFilter(RowFilter.regexFilter("(?i)" + text));
                }
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                String text = textField.getText();
                if (text.trim().length() == 0) {
                    rowSorter.setRowFilter(null);
                } else {
                    rowSorter.setRowFilter(RowFilter.regexFilter("(?i)" + text));
                }
            }

        });
        f.setVisible(true);
    }

    private static class Row {

        String lname;
        String fname;
        String monTime;
        String tueTime;
        String wedTime;
        String thuTime;
        String friTime;
        String satTime;
        String sunTime;
        String totalTime;
    }

    private static class JDBCModel extends AbstractTableModel {

        private final JProgressBar jpb = new JProgressBar();
        private static final long serialVersionUID = 1L;
        private final List<Row> data = new ArrayList<>();
        private ResultSet rs = null;
        private ResultSetMetaData meta;

        public JDBCModel(Connection conn, String query) {
            try {
                Statement s = conn.createStatement();
                rs = s.executeQuery(query);
                meta = rs.getMetaData();
                JDBCWorker worker = new JDBCWorker();
                jpb.setIndeterminate(true);
                worker.execute();

            } catch (SQLException e) {
                e.printStackTrace(System.err);
            }
        }

        @Override
        public int getRowCount() {
            return data.size();
        }

        @Override
        public int getColumnCount() {
            return 10;
        }

        @Override
        public Object getValueAt(int rowIndex, int colIndex) {
            Row row = data.get(rowIndex);
            switch (colIndex) {
                case 0:
                    return row.lname;
                case 1:
                    return row.fname;
                case 2:
                    return row.monTime;
                case 3:
                    return row.tueTime;
                case 4:
                    return row.wedTime;
                case 5:
                    return row.thuTime;
                case 6:
                    return row.friTime;
                case 7:
                    return row.satTime;
                case 8:
                    return row.sunTime;
                case 9:
                    return row.totalTime;
            }
            return null;
        }

        @Override
        public String getColumnName(int colIndex) {
            String name = null;
            switch (colIndex) {
                case 0:
                    name = "Last Name";
                    return name;
                case 1:
                    name = "First Name";
                    return name;
                case 2:
                    name = "Monday";
                    return name;
                case 3:
                    name = "Tuesday";
                    return name;
                case 4:
                    name = "Wednesday";
                    return name;
                case 5:
                    name = "Thursday";
                    return name;
                case 6:
                    name = "Friday";
                    return name;
                case 7:
                    name = "Saturday";
                    return name;
                case 8:
                    name = "Sunday";
                    return name;
                case 9:
                    name = "Total";
                    return name;
            }
            return name;
        }

        private class JDBCWorker extends SwingWorker<List<Row>, Row> {

            @Override
            protected List<Row> doInBackground() {
                try {
                    while (rs.next()) {
                        Row r = new Row();
                        Integer total = 0;
                        r.lname = rs.getString(2);
                        r.fname = rs.getString(1);
                        r.monTime = String.valueOf(rs.getInt(3) + " - " + rs.getInt(4));
                        total += rs.getInt(4) - rs.getInt(3);
                        r.tueTime = String.valueOf(rs.getInt(5) + " - " + rs.getInt(6));
                        total += rs.getInt(6) - rs.getInt(5);
                        r.wedTime = String.valueOf(rs.getInt(7) + " - " + rs.getInt(8));
                        total += rs.getInt(8) - rs.getInt(7);
                        r.thuTime = String.valueOf(rs.getInt(9) + " - " + rs.getInt(10));
                        total += rs.getInt(10) - rs.getInt(9);
                        r.friTime = String.valueOf(rs.getInt(11) + " - " + rs.getInt(12));
                        total += rs.getInt(12) - rs.getInt(11);
                        r.satTime = String.valueOf(rs.getInt(13) + " - " + rs.getInt(14));
                        total += rs.getInt(14) - rs.getInt(13);
                        r.sunTime = String.valueOf(rs.getInt(15) + " - " + rs.getInt(16));
                        total += rs.getInt(16) - rs.getInt(15);
                        r.totalTime = String.valueOf(total);
                        publish(r);
                    }
                } catch (SQLException e) {
                    e.printStackTrace(System.err);
                }
                return data;
            }

            @Override
            protected void process(List<Row> chunks) {
                int n = getRowCount() + 1;
                System.out.println("Row Count = " + n);
                System.out.println("Chunks Size = " + chunks.size());
                for (Row row : chunks) {
                    data.add(row);
                }
                fireTableDataChanged();
            }

            @Override
            protected void done() {
                jpb.setIndeterminate(false);
                jpb.setValue(100);
            }
        }
    }

//Creates the Test DB with test names and working hours
    private static void createTestDatabase(int n) {
        ThreadLocalRandom r = ThreadLocalRandom.current();
        Connection conn = getConnection();
        try {
            Statement st = conn.createStatement();
            st.execute("create table city(fname varchar(10), lname varchar(10), mons int(2), mone int(2), tues int(2), tuee int(2), weds int(2), wede int(2), thus int(2), thue int(2), fris int(2), frie int(2), sats int(2), sate int(2), suns int(2), sune int(2))");
            Random gen = new Random();
            PreparedStatement ps = conn.prepareStatement(
                "insert into city values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
            for (int i = 0; i < n; i++) {
                ps.setString(1, "First" + i);
                ps.setString(2, "Last" + i);
                ps.setInt(3, r.nextInt(6, 12));
                ps.setInt(4, r.nextInt(13, 22));
                ps.setInt(5, r.nextInt(6, 12));
                ps.setInt(6, r.nextInt(13, 22));
                ps.setInt(7, r.nextInt(6, 12));
                ps.setInt(8, r.nextInt(13, 22));
                ps.setInt(9, r.nextInt(6, 12));
                ps.setInt(10, r.nextInt(13, 22));
                ps.setInt(11, r.nextInt(6, 12));
                ps.setInt(12, r.nextInt(13, 22));
                ps.setInt(13, r.nextInt(6, 12));
                ps.setInt(14, r.nextInt(13, 22));
                ps.setInt(15, r.nextInt(6, 12));
                ps.setInt(16, r.nextInt(13, 22));
                ps.execute();
            }
        } catch (SQLException ex) {
            ex.printStackTrace(System.err);
        }
    }

    private static Connection getConnection() {
        try {
            return DriverManager.getConnection(URL, "", "");
        } catch (SQLException e) {
            e.printStackTrace(System.err);
        }
        return null;
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new WorkerTest()::display);
    }
}

【讨论】:

  • 我将fireTableRowsInserted() 行更改为fireTableDataChanged(),它运行良好...谢谢!
  • 很高兴你让它工作了;我添加了指向您问题的链接并引用了它here
  • 好东西!我有一个related follow-up question,如果有任何对此线程感兴趣的人愿意看一看,我会很高兴。谢谢。
猜你喜欢
  • 2016-02-20
  • 1970-01-01
  • 2011-01-21
  • 2014-11-06
  • 1970-01-01
  • 2012-06-30
  • 2013-12-17
  • 2012-02-26
  • 1970-01-01
相关资源
最近更新 更多