【问题标题】:Multiple row selection with checkbox in JTableJTable中带有复选框的多行选择
【发布时间】:2012-05-27 14:37:55
【问题描述】:

我有一个带有复选框的 JTable 作为列之一。我在标题中还有一个复选框来选中/取消选中所有。 AFAIK JTable 的默认行为是,如果选择新行,它会取消选择之前选择的所有行。但是我们可以通过复选框实现 CTRL 点击行为吗?即保留先前选择的行。 我面临的主要问题是使用复选框启用多个 JTable 行的选择。

预期输出

第一行被选中,然后第一行被选中,如果第三行被选中,那么第三行与第一行一起被选中(已经被选中)

实际输出

当第一行被选中并选择时,如果选择第三行,那么它会取消选择之前选择的所有行,并且只选择第三行。

我有一个示例代码,它模拟了我想要实现的场景,与 Add another One 按钮一样,但带有复选框选择。

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.table.AbstractTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableColumn;
import javax.swing.event.CellEditorListener;

public class JTableRowSelectProgramatically extends JPanel {

final JTable table = new JTable(new MyTableModel());

public JTableRowSelectProgramatically() {
    initializePanel();
}

private void initializePanel() {
    setLayout(new BorderLayout());
    setPreferredSize(new Dimension(475, 150));


    table.setFillsViewportHeight(true);
    JScrollPane pane = new JScrollPane(table);

    JLabel label2 = new JLabel("Row: ");
    final JTextField field2 = new JTextField(3);
    JButton add = new JButton("Select");

    table.setRowSelectionAllowed(true);
    table.setColumnSelectionAllowed(false);
    table.getSelectionModel().addListSelectionListener(new ListSelectionListenerImpl());
    TableColumn tc = table.getColumnModel().getColumn(3);
    tc.setCellEditor(table.getDefaultEditor(Boolean.class));
    tc.setCellRenderer(table.getDefaultRenderer(Boolean.class));
    ((JComponent) table.getDefaultRenderer(Boolean.class)).setOpaque(true);
    tc.getCellEditor().addCellEditorListener(new CellEditorListenerImpl());

    add.addActionListener(new ActionListener() {

        public void actionPerformed(ActionEvent event) {
            int index2 = 0;
            try {
                index2 = Integer.valueOf(field2.getText());
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
            table.addRowSelectionInterval(index2, index2);
            field2.setText(String.valueOf(index2));
        }
    });

    JPanel command = new JPanel(new FlowLayout());
    command.add(label2);
    command.add(field2);
    command.add(add);

    add(pane, BorderLayout.CENTER);
    add(command, BorderLayout.SOUTH);
}

public static void showFrame() {
    JPanel panel = new JTableRowSelectProgramatically();
    panel.setOpaque(true);

    JFrame frame = new JFrame("JTable Row Selection");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setContentPane(panel);
    frame.pack();
    frame.setVisible(true);
}

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

        public void run() {
            JTableRowSelectProgramatically.showFrame();
        }
    });
}

public class MyTableModel extends AbstractTableModel {

    private String[] columns = {"ID", "NAME", "AGE", "A STUDENT?"};
    private Object[][] data = {
        {1, "Alice", 20, new Boolean(false)},
        {2, "Bob", 10, new Boolean(false)},
        {3, "Carol", 15, new Boolean(false)},
        {4, "Mallory", 25, new Boolean(false)}
    };

    public int getRowCount() {
        return data.length;
    }

    public int getColumnCount() {
        return columns.length;
    }

    public Object getValueAt(int rowIndex, int columnIndex) {
        return data[rowIndex][columnIndex];
    }

    @Override
    public String getColumnName(int column) {
        return columns[column];
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return columnIndex == 3;
    }

    //
    // This method is used by the JTable to define the default
    // renderer or editor for each cell. For example if you have
    // a boolean data it will be rendered as a check box. A
    // number value is right aligned.
    //
    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return data[0][columnIndex].getClass();
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        if (columnIndex == 3) {
            data[rowIndex][columnIndex] = aValue;
            fireTableCellUpdated(rowIndex, columnIndex);
        }
    }
}

class ListSelectionListenerImpl implements ListSelectionListener {

    public void valueChanged(ListSelectionEvent lse) {
        ListSelectionModel lsm = (ListSelectionModel) lse.getSource();
        int row = table.getRowCount();
        if (lsm.isSelectionEmpty()) {
        } else {
//                If any column is clicked other than checkbox then do normal selection
//                i.e select the click row and deselects the previous selection
            if (table.getSelectedColumn() != 3) {
                for (int i = 0; i < row; i++) {
                    if (lsm.isSelectedIndex(i)) {
                        table.setValueAt(true, i, 3);
                    } else {
                        table.setValueAt(false, i, 3);
                    }
                }

            }
        }
    }
  }
public class CellEditorListenerImpl implements CellEditorListener{

    public void editingStopped(ChangeEvent e) {
        for(int i=0; i<table.getRowCount();i++){
            if((Boolean)table.getValueAt(i, 3)){
                table.addRowSelectionInterval(i, i);
            }
            else{
                table.removeRowSelectionInterval(i, i);
            }
        }
    }

    public void editingCanceled(ChangeEvent e) {
        System.out.println("do nothing");
    }

}
}

【问题讨论】:

  • 好问题。 +1 不立即知道答案,但其中一位餐桌大师应该很快就会出现。
  • 我刚刚注意到有人否决了这个问题!愿意分享你的推理吗?我很困惑。
  • @AndrewThompson 想知道为什么这个问题被否决了:)
  • +1 表示sscce 并使用invokeLater()

标签: java swing jtable


【解决方案1】:

实现这些TableModel 方法后,您可以在按钮侦听器中使用setValueAt() 来根据需要调整模型,以保持复选框状态和选择模型同步。有一个相关的例子here

@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
    return columnIndex == 3;
}

@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
    if (columnIndex == 3) {
        data[rowIndex][columnIndex] = aValue;
        fireTableCellUpdated(rowIndex, columnIndex);
    }
}

附录:作为一个具体示例,您的clear 侦听器可能会调用TableModel 中的方法,例如clearChecks()

MyTableModel model = (MyTableModel) table.getModel();
model.clearChecks();
...
private void clearChecks() {
    for (int i = 0; i < data.length; i++) {
        data[i][3] = false;
    }
    fireTableRowsUpdated(0, data.length);
}

【讨论】:

  • 感谢您的回答。但是有一件事我们如何控制行的选择?我已经在我的应用程序中实现的是 -Check/UnCheck All rows with header 复选框 - 可以选中该复选框,但不会突出显示我尝试过您的代码的行,但我可以检查它但未突出显示选中的行。我作为 sscce 提供的代码只是一个模拟。我想要添加另一个按钮的相同功能,但通过复选框
  • 你必须操纵表格的ListSelectionModel;当我不在时,请更新您的问题代码以反映我提出的更改以及您之后所做的任何更改。
  • 实施了您的建议并编辑了 sscce 但仍然有一个问题是我想根据复选框选择来选择行。即如果我选中复选框,则应选中该行并取消选中该复选框,则应取消选中该行,并且应应用多行而不仅仅是单行。
  • clearChecks() 方法应该进入您的模型并从您的(现在缺失的)clear 处理程序中调用。您还应该恢复您的 select 处理程序并添加类似的 syncChecks()
  • 您能分享一下您对 ListSelectionListener 的实现吗?
【解决方案2】:

在高层次上,我会调用一个键侦听器并检查 VK_CTRL 在您的选择侦听器中是否被标记。这听起来像是逻辑块问题或侦听器冲突。

除此之外,JList 框允许进行多项选择。

【讨论】:

  • 您能否详细说明检查 VK_CTRL 是什么意思。如果复选框被选中,我想选择该行,反之亦然。由于复选框是 JTable 中布尔类的默认编辑器,这意味着我应该为复选框编写一个单独的编辑器并在那里添加 keylistener 还是应该将 keylistener 添加到 JTable。如果您能阐明您的方法,我将非常感激。
  • 当您说类似 CTRL 的行为时,我认为您希望能够一次选中多个框以影响它们的特定行。
  • 在这种情况下,我会创建一个类,其中复选框是该表的父级。因此,抽象类将删除一些乏味的 field2,例如您拥有的变量。它可以被重写为简单地更改名为 MY_Table_Row 或类似的子类中的布尔值。我有一种预感,是命名不匹配导致了这些怪癖。
  • 此外,您的复选框似乎有一种单选按钮行为,因为修改一个修改其他的状态。也许,除了我正在推广的这个子类之外,将这些类的复选框的状态存储在一个包含所有选定复选框的数组中。
  • 对不起,如果我不清楚我的问题。类似 CTRL 的行为意味着如果我已经选择了一行,那么如果我单击另一行,那么通常简单的单击将取消选择先前的选择,但 CTRL 单击将保留先前的选择。如果已经选择了任何行,则希望对复选框执行相同的操作,然后保留其选择并选择新的。
猜你喜欢
  • 1970-01-01
  • 2020-10-17
  • 1970-01-01
  • 1970-01-01
  • 2011-05-30
  • 2023-04-06
  • 2011-05-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多