将 JTable 的 selection 行为更改为仅 select 行或列中可变数量的单元格

Alter selection behavior for a JTable to only select a variable number of cells in a row or column

我有一个 JTable,我在其中切换启用行或列选择。此功能运行良好,但我希望能够确定与当前选定单元格相邻的突出显示单元格的数量。当前,整行或整列都处于选中状态。我试图添加一个 ListSelectionModel 来实现此功能,但它只允许选择整行或整列。以下是示例图片:

ADJACENTSELECTION 默认设置为两个,因此我想在启用 rowSelection 时突出显示所选单元格左右两侧的 2 个单元格,或者在 rowSelection 为假时突出显示所选单元格上方和下方的 2 个单元格。任何帮助将不胜感激。这是我的代码:

package selectionTest;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.border.Border;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;

public class App {

    private void display() {
        JFrame f = new JFrame("Demo");
        JPanel gui = new JPanel(new BorderLayout());
        GridTable gridTable = new GridTable(25, 25);
        gui.add(gridTable, BorderLayout.CENTER);
        f.add(gui);
        f.setBackground(Color.BLUE);
        gui.setBackground(Color.WHITE);
        f.setLocationByPlatform(true);
        f.pack();
        f.setSize(500, 600);
        f.setVisible(true);

    }

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

}

class GridTable extends JTable {

    static int ADJACENTSELECTION = 2;
    boolean rowSelection = false;
    int rows, cols;

    public GridTable(int rows, int cols) {
        this.rows = rows;
        this.cols = cols;
        this.setModel(new DefaultTableModel(rows, cols));
        this.setShowGrid(true);
        Border blackline = BorderFactory.createLineBorder(Color.black);
        this.setBorder(blackline);
        this.setGridColor(Color.black);
        ActionMap map = this.getActionMap();
        Action action = switchOrientation();
        String keyStrokeAndKey = "control O";
        KeyStroke keyStroke = KeyStroke.getKeyStroke(keyStrokeAndKey);
        this.getInputMap().put(keyStroke, keyStrokeAndKey);
        this.getActionMap().put(keyStrokeAndKey, action);
        this.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent event) {
                int row = getSelectedRow();
                int column = getSelectedColumn();
                if (rowSelection) {
                    setRowSelectionInterval(Math.max(0, row - ADJACENTSELECTION), Math.min(row, row + ADJACENTSELECTION));
                } else {
                    setColumnSelectionInterval(Math.max(0, column - ADJACENTSELECTION), Math.min(column, column + ADJACENTSELECTION));
                }
            }
        });
    }

    private AbstractAction switchOrientation() {
        AbstractAction switchOrientation = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                rowSelection = !rowSelection;
                setColumnSelectionAllowed(rowSelection);
                setRowSelectionAllowed(!rowSelection);
            }
        };
        return switchOrientation;
    }

}

您就快完成了,只需几个额外的步骤,您可以更新选择侦听器以仅突出显示特定单元格:

  1. 通过将 this.setCellSelectionEnabled(true); 添加到您的 GridTable 方法来允许选择特定单元格:

    public GridTable(int rows, int cols) {
        this.setCellSelectionEnabled(true);
        //...
    
  2. 确保 SelectionInterval ranges/values 正确:

    if (rowSelection == true){
        //Correct using `getRowCount()` for selection range
        setRowSelectionInterval(Math.max(0, row - ADJACENTSELECTION), Math.min(getRowCount()-1, row + ADJACENTSELECTION));
    }
    else{
        //Correct using `getColumnCount()` for selection range
        setColumnSelectionInterval(Math.max(0, column - ADJACENTSELECTION), Math.min(getColumnCount()-1, column + ADJACENTSELECTION));
    }
    
  3. 确保更新选择时不会再次触发选择事件。您可以通过使用布尔值来跟踪选择事件来做到这一点:

    this.getSelectionModel().addListSelectionListener(new ListSelectionListener(){
        //Use this to track changes
        boolean initial = true;
        //...
        public void valueChanged(ListSelectionEvent e){
            if (initial){
                int column = getSelectedColumn();
                int row = getSelectedRow();
                //...
    
  4. 最后,将它们放在一起,这是完整的选择侦听器(并且不要忘记将 this.setCellSelectionEnabled(true); 添加到 GridTable):

    this.getSelectionModel().addListSelectionListener(new ListSelectionListener(){
        //Use this to track changes
        boolean initial = true;
    
        public void valueChanged(ListSelectionEvent e){
            //Only change value on first select (This prevents the below from triggering itself)
            if (initial){
                int column = getSelectedColumn();
                int row = getSelectedRow();
                initial = false;
                if (rowSelection == true){
                    setRowSelectionInterval(Math.max(0, row - ADJACENTSELECTION), Math.min(getRowCount()-1, row + ADJACENTSELECTION));
                }
                else{
                    setColumnSelectionInterval(Math.max(0, column - ADJACENTSELECTION), Math.min(getColumnCount()-1, column + ADJACENTSELECTION));
                }
            }
            //Set the value back once a selection is complete
            else{
                initial = true;
            }
        }
    });
    

这将突出显示所选单元格,以及所选内容两侧的两个单元格