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

Android扫雷游戏调整minesSeekBar后莫名新增一行按钮问题

扫雷游戏UI异常问题描述

这是一款由按钮构成雷区的扫雷游戏,雷区的gridSize设为4,gridHeight设为8。首次初始化可正常显示4×8的按钮区域,但首次游戏结束后,调整minesSeekBar时,gridHeight未发生修改,雷区却新增了一行按钮。

初始效果:首次游戏界面
异常效果:调整后的界面

无法理解为何gridHeight未修改却新增了一行按钮,怀疑问题出在start.setOnClickListener()中,但未找到修复点,理论上每次点击Start都会重置雷区,应该保持一致。认为setupGame()中不存在问题,因为该方法未修改gridSize。


XML布局代码

<Button
    android:id="@+id/menu1"
    android:layout_width="55dp"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:layout_marginStart="340dp"
    android:layout_marginTop="10dp"
    android:background="@drawable/baseline_menu_24"
    app:cornerRadius="15dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.0" />

<Button
    android:id="@+id/start"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="START"
    android:layout_marginTop="5dp"
    android:layout_marginStart="0dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.498"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.005"
    tools:ignore="MissingConstraints" />

<EditText
    android:layout_marginStart="10dp"
    android:id="@+id/einsatz"
    android:layout_marginTop="10dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:hint="Einsatz"
    android:inputType="number"
    android:textColor="@color/black"
    android:textColorHighlight="@color/white"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />


<SeekBar
    android:id="@+id/sizeSeekBar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:max="8"
    android:progress="2"
    app:layout_constraintBottom_toTopOf="@+id/gridlayout"
    app:layout_constraintTop_toTopOf="parent"
    tools:layout_editor_absoluteX="10dp"
android:visibility="gone"/> 

<SeekBar
    android:id="@+id/minesSeekBar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:max="16"
    android:progress="2"
    app:layout_constraintBottom_toTopOf="@+id/gridlayout"
    app:layout_constraintTop_toBottomOf="@+id/start"
    tools:layout_editor_absoluteX="10dp" />

<GridLayout
    android:id="@+id/gridlayout"
    android:layout_width="match_parent"
    android:layout_height="400dp"
    android:layout_marginBottom="52dp"
    android:layout_weight="1"
    android:background="@drawable/custom_edittext"
    android:columnCount="4"
    android:rowCount="4"
    app:layout_constraintBottom_toBottomOf="parent"
    tools:layout_editor_absoluteX="9dp" />

Java主类代码

public class Minen extends AppCompatActivity {

private int gridSize = 4; // 默认网格行数
private int gridHeight = 8; // 默认网格列数
private int numMines = 2; // 默认地雷数量
private ArrayList<ArrayList<Button>> buttons;
private ArrayList<ArrayList<Boolean>> minePositions;
private boolean gameLost = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_minefield);
    getSupportActionBar().hide();
    Button menu = findViewById(R.id.menu1);
    GridLayout gridLayout = findViewById(R.id.gridlayout);
    SeekBar heightSeekBar = findViewById(R.id.sizeSeekBar);
    SeekBar minesSeekBar = findViewById(R.id.minesSeekBar);
    Button start = findViewById(R.id.start);
    // 设置高度滑块监听
    heightSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
           int x = progress + 1; // 调整范围为2到10
            setupGame();
        }
        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {}
        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
        }
    });
    minesSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            numMines = progress;
            setupGame();
        }
        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {}
        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {}
    });

    // 初始化按钮和地雷位置集合
    buttons = new ArrayList<>();
    minePositions = new ArrayList<>();

    start.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (getEinsatzFromEditText() > 0) {
                kontostandpositiv(getEinsatzFromEditText(), new Callback() {
                    @Override
                    public void Callbackchecked(boolean kontostandPositiv) {
                        if (kontostandPositiv) {
                            kontostandandernminus(getEinsatzFromEditText());
                            for (int i = 0; i < gridSize; i++) {
                                ArrayList<Button> rowButtons = new ArrayList<>();
                                ArrayList<Boolean> rowMinePositions = new ArrayList<>();
                                for (int j = 0; j < gridHeight; j++) {
                                    Button button = new Button(getApplicationContext());
                                    int finalI = i;
                                    int finalJ = j;
                                    button.setOnClickListener(new View.OnClickListener() {
                                        @Override
                                        public void onClick(View v) {
                                            if (!gameLost) {
                                                if (minePositions.get(finalI).get(finalJ)) {
                                                    gameLost = true;
                                                    showGameOver();
                                                } else {
                                                    button.setEnabled(false);
                                                    calcwin();
                                                    checkForWin();
                                                }
                                            }
                                        }
                                    });
                                    rowButtons.add(button);
                                    rowMinePositions.add(false);
                                    gridLayout.addView(button);
                                }
                                buttons.add(rowButtons);
                                minePositions.add(rowMinePositions);
                            }

                            setupGame();
                        } else {
                            Toast.makeText(Minen.this, "余额不足", Toast.LENGTH_SHORT).show();
                        }
                    }
                });
            } else {
                Toast.makeText(Minen.this, "请设置赌注", Toast.LENGTH_SHORT).show();
            }
        }
    });

相关方法代码

setupGame()

private void setupGame() {
    gameLost = false;

    // 重置按钮状态和地雷位置
    for (int i = 0; i < gridSize; i++) {
        for (int j = 0; j < gridHeight; j++) {
            Button button = buttons.get(i).get(j);
            button.setEnabled(true);
            button.setText("");
            minePositions.get(i).set(j, false);
        }
    }

    // 随机生成地雷位置
    generateMinePositions();
}

generateMinePositions()

private void generateMinePositions() {
    Random random = new Random();
    int minesPlaced = 0;

    while (minesPlaced < numMines) {
        int row = random.nextInt(4);
        int col = random.nextInt(gridHeight);

        if (!minePositions.get(row).get(col)) {
            minePositions.get(row).set(col, true);
            minesPlaced++;
        }
    }
}

showGameOver()

private void showGameOver() {
    Toast.makeText(this, "游戏结束!你踩到了地雷。", Toast.LENGTH_SHORT).show();
    for (int i = 0; i < gridSize; i++) {
        for (int j = 0; j < gridHeight; j++) {
            if (minePositions.get(i).get(j)) {
                buttons.get(i).get(j).setText("X");
            }
            buttons.get(i).get(j).setEnabled(false);
        }
    }
}

checkForWin()

private void checkForWin() {
    for (int i = 0; i < gridSize; i++) {
        for (int j = 0; j < gridHeight; j++) {
            if (!minePositions.get(i).get(j) && buttons.get(i).get(j).isEnabled()) {
                return; // 还有未翻开的非地雷格子
            }
        }
    }

    Toast.makeText(this, "恭喜!你获胜了!", Toast.LENGTH_SHORT).show();
}

问题原因与修复方案

核心问题

每次点击Start按钮时,你都在重复向GridLayout中添加新按钮,但没有先清空之前的按钮和数据集合。调整minesSeekBar触发setupGame()时,虽然重置了现有按钮状态,但之前多次点击Start积累的按钮还留在GridLayout里,导致显示行数异常。

另外还有两个次要问题:

  1. heightSeekBar的监听逻辑错误,没有将进度值赋值给gridHeight,导致滑动该SeekBar不会改变雷区高度。
  2. generateMinePositions()中固定用random.nextInt(4)生成行索引,应该改用gridSize变量,适配动态行数。

修复步骤

  1. 点击Start前清空GridLayout与数据集合
    在Start按钮的点击事件中,先清空buttons、minePositions集合,同时移除GridLayout中的所有子View:

    start.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (getEinsatzFromEditText() > 0) {
                kontostandpositiv(getEinsatzFromEditText(), new Callback() {
                    @Override
                    public void Callbackchecked(boolean kontostandPositiv) {
                        if (kontostandPositiv) {
                            kontostandandernminus(getEinsatzFromEditText());
                            // 新增:清空旧数据和视图
                            buttons.clear();
                            minePositions.clear();
                            gridLayout.removeAllViews();
                            
                            for (int i = 0; i < gridSize; i++) {
                                // 原有生成按钮的代码...
                            }
    
                            setupGame();
                        } else {
                            Toast.makeText(Minen.this, "余额不足", Toast.LENGTH_SHORT).show();
                        }
                    }
                });
            } else {
                Toast.makeText(Minen.this, "请设置赌注", Toast.LENGTH_SHORT).show();
            }
        }
    });
    
  2. 修复heightSeekBar的赋值逻辑
    修改heightSeekBar的监听,将进度值正确赋值给gridHeight:

    heightSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            gridHeight = progress + 1; // 调整范围为2到10
            setupGame();
        }
        // 其他方法不变...
    });
    
  3. 修复generateMinePositions()的行索引生成
    把固定的4改为gridSize:

    private void generateMinePositions() {
        Random random = new Random();
        int minesPlaced = 0;
    
        while (minesPlaced < numMines) {
            int row = random.nextInt(gridSize); // 改用gridSize变量
            int col = random.nextInt(gridHeight);
    
            if (!minePositions.get(row).get(col)) {
                minePositions.get(row).set(col, true);
                minesPlaced++;
            }
        }
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 13:25:55