You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java实现按钮矩阵相邻按钮值交换功能求助(附代码)

实现按钮矩阵的相邻交换功能(无需Thread)

嘿,其实你完全不需要用Thread来实现这个功能!Swing的事件处理本身就是在单线程(事件调度线程)里运行的,我们只需要用一个变量记录第一个被点击的按钮,就能完成两次点击的状态交换逻辑。下面是修改后的完整代码,我会一步步解释关键改动:

核心思路

  • 用一个变量记录第一个被点击的按钮,作为交换的起点
  • 点击按钮时分两种情况:
    1. 还没选中任何按钮:标记当前按钮为选中状态(给个视觉反馈,比如变灰)
    2. 已经选中了一个按钮:检查当前按钮和选中按钮是否相邻,是则交换状态,然后清除选中标记
  • 实现相邻判断逻辑,确保只有上下左右的按钮可以交换
  • 交换后更新两个按钮的显示文本和状态

修改后的完整代码

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class ButtonsMatrix extends JButton {
    private int[][] fModel;
    private int fX;
    private int fY;
    // 记录第一个选中的按钮
    private static ButtonsMatrix firstSelected = null;

    public ButtonsMatrix(int x, int y, int[][] model) {
        fX = x;
        fY = y;
        fModel = model;
        // 添加点击事件监听器
        addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                handleButtonClick();
            }
        });
        updateNameFromModel();
    }

    // 处理按钮点击逻辑
    private void handleButtonClick() {
        if (firstSelected == null) {
            // 第一次点击:标记当前按钮为选中状态
            firstSelected = this;
            setBackground(Color.LIGHT_GRAY); // 视觉反馈
        } else {
            // 第二次点击:检查是否是同一个按钮
            if (firstSelected == this) {
                // 点击同一个按钮,取消选中
                firstSelected.setBackground(null);
                firstSelected = null;
                return;
            }
            // 检查是否相邻
            if (isAdjacent(firstSelected)) {
                // 交换状态值
                int temp = fModel[fX][fY];
                fModel[fX][fY] = fModel[firstSelected.fX][firstSelected.fY];
                fModel[firstSelected.fX][firstSelected.fY] = temp;
                // 更新两个按钮的显示
                firstSelected.updateNameFromModel();
                this.updateNameFromModel();
                // 清除选中状态的视觉反馈
                firstSelected.setBackground(null);
            }
            // 无论是否交换,都清除选中标记
            firstSelected = null;
        }
    }

    // 判断当前按钮和另一个按钮是否相邻(上下左右)
    private boolean isAdjacent(ButtonsMatrix other) {
        int rowDiff = Math.abs(fX - other.fX);
        int colDiff = Math.abs(fY - other.fY);
        // 相邻条件:行差为1且列差为0,或者列差为1且行差为0
        return (rowDiff == 1 && colDiff == 0) || (colDiff == 1 && rowDiff == 0);
    }

    private void updateNameFromModel() {
        setText(fModel[fX][fY] == 1 ? "Red" : "Blue");
        // 按钮背景色直接对应状态,更直观
        setBackground(fModel[fX][fY] == 1 ? Color.RED : Color.BLUE);
        setOpaque(true);
        setBorderPainted(true);
    }

    public static void main(String[] args) {
        // 确保在Swing事件调度线程中运行GUI代码
        SwingUtilities.invokeLater(() -> {
            int dim = 7;
            int[][] matrix = new int[dim][dim];
            // 初始化矩阵的随机状态(红1,蓝2)
            for (int r = 0; r < dim; r++) {
                for (int c = 0; c < dim; c++) {
                    matrix[r][c] = (int) (Math.random() * 2) + 1; // 生成1或2
                }
            }

            JFrame f = new JFrame("Button Matrix Swap");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JPanel p = new JPanel(new GridLayout(dim, dim, 2, 2)); // 按钮间加一点间距

            for (int r = 0; r < dim; r++) {
                for (int c = 0; c < dim; c++) {
                    ButtonsMatrix button = new ButtonsMatrix(r, c, matrix);
                    p.add(button);
                }
            }

            f.add(p);
            f.pack();
            f.setLocationRelativeTo(null); // 窗口居中显示
            f.setVisible(true);
        });
    }
}

关键改动说明

  1. 选中状态记录:用firstSelected静态变量存储第一个点击的按钮,方便第二次点击时对比
  2. 点击逻辑拆分:handleButtonClick方法清晰区分第一次和第二次点击的行为,逻辑更清晰
  3. 相邻判断:isAdjacent方法严格检查两个按钮是否为上下左右相邻,避免无效交换
  4. 视觉优化:
    • 按钮背景色直接对应状态(红/蓝),比纯文本更直观
    • 选中的按钮会变灰,给用户明确的操作反馈
  5. 初始化优化:在main方法里统一初始化矩阵的随机状态,避免每个按钮构造时重复随机赋值
  6. 线程安全:用SwingUtilities.invokeLater确保GUI代码在事件调度线程中运行,符合Swing的线程规范

额外建议

  • 避免静态变量:如果你的程序需要多个矩阵实例,静态变量会出问题,建议把firstSelected移到主类或者单独的控制器类中管理
  • 扩展功能:可以添加音效或状态切换动画,提升用户体验
  • 状态持久化:如果需要保存矩阵状态,可以把fModel序列化或者保存到本地文件
  • 错误提示:如果点击非相邻按钮,可以弹出提示框告知用户无法交换

内容的提问来源于stack exchange,提问作者Bagdan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:06:14