【问题标题】:Get Selected Rows in JTable using AbstractTableModel使用 AbstractTableModel 获取 JTable 中的选定行
【发布时间】:2012-12-04 14:28:41
【问题描述】:

我有一个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);
     }
}

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

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

有没有更好的方法?

【问题讨论】:

  • 增强模型:它可以在 setValueAt(...) 中保持/同步包含其“选定”(又名:在第一列中具有布尔值 true)的内部数据结构
  • 谢谢。但是,要检索布尔值为真的那个,我需要遍历吗?
  • 如果您使用的是 TableModel,则基本上可以使用相同的方法来保留选定行的数组。唯一不同的是你可以从 TableModel 的 setValueAt() 方法中更新它。
  • @Che 在用户选择了一些行之后发生了什么,她/他按下了某个 Jbutton,或者有一些自动 :-),如果有 user_action(JButtons 点击),那么不使用任何增强功能,将 RowFilter 与 String ("true") 一起用于所需列
  • 确定 RowFilter 也可以使用 Boolean、Integer、Date ... 值,但我只使用 String value

标签: java swing jtable listener jcheckbox


【解决方案1】:

在下面的示例中,TableModelsetValueAt() 的实现中更新了Set&lt;Integer&gt; checked。相邻JList 的模型监听表的模型并显示当前选择的行号。该示例假定所选行数与行数相比较小。注意TreeSet 的使用,它的迭代器保留了元素的自然顺序。

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;
        }
    }
}

【讨论】:

  • +1 .. 在模型本身中编写逻辑(获取选定的行)是一种好习惯。 B'coz 这些天我把 TableModel 从我的逻辑中分离出来。
  • 这是一个权衡,当然值得为大型模型进行分析。还可以考虑将AbstractListModel 用于JListshared model
  • 但是我对这种方法有疑问。当我删除一些选定的行时,必须从集合中删除这些行号。但是 Set 中具有先前行号的所有行号都会不一致,对吗?我有删除、do_action 等功能。
  • 好的,我认为一个解决方案是.. 每当我删除一行时,我都会一次重新创建 Set。所以它会再次回到与所有选定行一致的状态。
  • 是的,这就是权衡:一旦您确定每次重新创建 Set 的成本太高,那么您必须使 Set 保持最新以进行所有插入、更新或删除操作。您的AbstractTableModel 就是这样做的地方。 Set&lt;Integer&gt; 只是为了演示;您可能想查看Set&lt;Row&gt; 或其他支持您预期操作的数据结构。
【解决方案2】:

我同意克娄巴特拉。当您创建 AbstractTableModel 的子类时,您将覆盖 setValue(Object value, int rowIndex, int colIndex)。在您覆盖的方法中,您只需检查该列是否是带有您的复选框的列,如果是,则适当地更新内部数据结构。您还可以添加一个 getCheckedRows() 方法,该方法返回一个 List,其中包含已选中复选框的行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-04
    • 2016-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-03
    相关资源
    最近更新 更多