java 使用 AbstractTableModel 获取 JTable 中的选定行

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13915081/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 14:25:51  来源:igfitidea点击:

Get Selected Rows in JTable using AbstractTableModel

javaswingjtablelistenerjcheckbox

提问by Amarnath

I have a JTableusing AbstractTableModelwhere I have a JCheckBoxin the first column for selecting rows. Now, I need to get the selected rows from the table which are checked. Right now, I am sequentially traversing from first row to the last row and getting all the rows that are selected like the following,

我有一个JTable使用AbstractTableModel在那里我有一个JCheckBox在选择行的第一列。现在,我需要从已检查的表中获取选定的行。现在,我从第一行到最后一行依次遍历并获取所有被选中的行,如下所示,

List<Integer> selectedRows = new ArrayList<Integer>();
for(int i = 0; i < table.getRowCount(); i++) {
     if((Boolean) table.getValuAt(i, 0)) {
         selectedRows.add(i);
     }
}

The problem here is, I need to traverse all the rows when ever I need to get the selected rows. Right now I am having 10 to 20 rows. But in future I will get around 5000 rows. My question is, if there are 5000 rows and if the user selects only 5000nd (last record) row then I need to traverse all the 5000 rows to get the selected row. Which I think is not a good approach.

这里的问题是,当我需要获取所选行时,我需要遍历所有行。现在我有 10 到 20 行。但将来我会得到大约 5000 行。我的问题是,如果有 5000 行,并且用户只选择第 5000 行(最后一条记录),那么我需要遍历所有 5000 行以获取所选行。我认为这不是一个好方法。

One approach which I want to implement is, to add a listener to the JCheckBoxcolumn, such that when ever there is a change (SELECTED/DESELECTED)then I need to update my array of the selected rows in the listener class. In this listener class when ever user selectes a JCheckBoxI need to call table.getSelectedRow(..)and I need to store if that JCheckBoxis selected.

我想要实现的一种方法是向JCheckBox列添加一个侦听器,这样每当发生更改时,(SELECTED/DESELECTED)我都需要更新侦听器类中所选行的数组。在这个侦听器类中,当用户选择JCheckBox我需要调用的一个时table.getSelectedRow(..),如果JCheckBox选择了我需要存储。

Are there any better approaches ?

有没有更好的方法?

回答by trashgod

In the example below, the TableModelupdates a Set<Integer> checkedin the implementation of setValueAt(). The model of an adjacent JListlistens to the table's model and displays the currently selected row numbers. The example assumes that the number of selected rows is small compared to the number of rows. Note the use of TreeSet, whose iterator retains the natural order of the elements.

在下面的例子中,TableModel更新了Set<Integer> checked在执行setValueAt()。相邻JList的模型监听表的模型并显示当前选择的行号。该示例假设所选行数与行数相比较小。注意 的使用TreeSet,它的迭代器保留了元素的自然顺序。

image

图片

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;

/** @see http://stackoverflow.com/a/13919878/230513 */
public class CheckTable {

    private static final CheckModel model = new CheckModel(5000);
    private static final JTable table = new JTable(model) {

        @Override
        public Dimension getPreferredScrollableViewportSize() {
            return new Dimension(150, 300);
        }
    };

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame f = new JFrame("CheckTable");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.setLayout(new GridLayout(1, 0));
                f.add(new JScrollPane(table));
                f.add(new DisplayPanel(model));
                f.pack();
                f.setLocationRelativeTo(null);
                f.setVisible(true);
            }
        });
    }

    private static class DisplayPanel extends JPanel {

        private DefaultListModel dlm = new DefaultListModel();
        private JList list = new JList(dlm);

        public DisplayPanel(final CheckModel model) {
            super(new GridLayout());
            this.setBorder(BorderFactory.createTitledBorder("Checked"));
            this.add(new JScrollPane(list));
            model.addTableModelListener(new TableModelListener() {

                @Override
                public void tableChanged(TableModelEvent e) {
                    dlm.removeAllElements();
                    for (Integer integer : model.checked) {
                        dlm.addElement(integer);
                    }
                }
            });
        }
    }

    private static class CheckModel extends AbstractTableModel {

        private final int rows;
        private List<Boolean> rowList;
        private Set<Integer> checked = new TreeSet<Integer>();

        public CheckModel(int rows) {
            this.rows = rows;
            rowList = new ArrayList<Boolean>(rows);
            for (int i = 0; i < rows; i++) {
                rowList.add(Boolean.FALSE);
            }
        }

        @Override
        public int getRowCount() {
            return rows;
        }

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

        @Override
        public String getColumnName(int col) {
            return "Column " + col;
        }

        @Override
        public Object getValueAt(int row, int col) {
            if (col == 0) {
                return row;
            } else {
                return rowList.get(row);
            }
        }

        @Override
        public void setValueAt(Object aValue, int row, int col) {
            boolean b = (Boolean) aValue;
            rowList.set(row, b);
            if (b) {
                checked.add(row);
            } else {
                checked.remove(row);
            }
            fireTableRowsUpdated(row, row);
        }

        @Override
        public Class<?> getColumnClass(int col) {
            return getValueAt(0, col).getClass();
        }

        @Override
        public boolean isCellEditable(int row, int col) {
            return col == 1;
        }
    }
}

回答by Rob Philipp

I agree with kleopatra. When you create a subclass of the AbstractTableModel, you'll override the setValue( Object value, int rowIndex, int colIndex ). In your overridden method, you just check if the column is the one with your check box, and if so, update the internal data structure appropriately. You can also add a method getCheckedRows() that returns a List< Integer > with the rows in which the check boxes have been selected.

我同意克利奥帕特拉的观点。当您创建 AbstractTableModel 的子类时,您将覆盖 setValue( Object value, int rowIndex, int colIndex )。在您的重写方法中,您只需检查该列是否与您的复选框相同,如果是,则相应地更新内部数据结构。您还可以添加一个方法 getCheckedRows(),该方法返回一个 List<Integer>,其中包含已选中复选框的行。