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

基于JavaFX(JDK8,无FXML)的俄罗斯方块自定义按键绑定实现问询

JavaFX 8 自定义按键绑定配置实现方案

核心思路

  • 为每个控制按钮维护监听状态标记,区分当前是激活监听还是取消监听
  • 全局注册KeyEvent.KEY_PRESSED监听器,仅在有按钮处于激活状态时生效
  • 捕获首个按键后更新对应控制项的KeyCode变量,同时重置所有按钮状态并移除全局监听器

具体实现步骤

  1. 定义全局变量:存储各控制项的KeyCode、当前激活的控制项标识、全局按键监听器引用
  2. 构建设置菜单UI:为每个控制项创建Label+Button组合,绑定按钮点击事件
  3. 按钮点击逻辑:切换监听状态,激活时标记控制项并注册全局监听器,取消时重置状态并移除监听器
  4. 全局按键监听逻辑:捕获按键后更新KeyCode,恢复按钮状态并移除监听器

完整代码示例

1. 定义控制项枚举

// 用枚举区分不同控制操作,避免硬编码
private enum ControlType {
    MOVE_LEFT, MOVE_RIGHT, ROTATE, SOFT_DROP, HARD_DROP, HOLD
}

2. 全局变量声明

// 存储各控制项的默认按键配置
private KeyCode moveLeftKey = KeyCode.LEFT;
private KeyCode moveRightKey = KeyCode.RIGHT;
private KeyCode rotateKey = KeyCode.UP;
private KeyCode softDropKey = KeyCode.DOWN;
private KeyCode hardDropKey = KeyCode.SPACE;
private KeyCode holdKey = KeyCode.SHIFT;

// 当前激活的控制项(null表示无监听状态)
private ControlType activeControl = null;
// 全局按键监听器引用,用于后续移除操作
private EventHandler<KeyEvent> keyListener;

3. 构建设置菜单UI

// 创建设置菜单的主布局
VBox settingsLayout = new VBox(10);
settingsLayout.setPadding(new Insets(20));

// 逐个添加控制项的Label和Button
addControlItem(settingsLayout, "左移", ControlType.MOVE_LEFT);
addControlItem(settingsLayout, "右移", ControlType.MOVE_RIGHT);
addControlItem(settingsLayout, "旋转", ControlType.ROTATE);
addControlItem(settingsLayout, "软降", ControlType.SOFT_DROP);
addControlItem(settingsLayout, "硬降", ControlType.HARD_DROP);
addControlItem(settingsLayout, "暂存", ControlType.HOLD);

// 初始化Stage和Scene
Scene settingsScene = new Scene(settingsLayout, 300, 400);
Stage settingsStage = new Stage();
settingsStage.setTitle("自定义控制设置");
settingsStage.setScene(settingsScene);
settingsStage.show();

4. 控制项UI生成与事件绑定

private void addControlItem(VBox parent, String labelText, ControlType controlType) {
    HBox itemRow = new HBox(10);
    itemRow.setAlignment(Pos.CENTER_LEFT);

    Label label = new Label(labelText + ": " + getCurrentKeyCode(controlType));
    Button bindButton = new Button("设置按键");

    // 按钮点击事件逻辑
    bindButton.setOnAction(e -> {
        if (activeControl == null) {
            // 激活监听状态
            activeControl = controlType;
            bindButton.setText("取消监听");
            label.setText(labelText + ": 等待按键...");

            // 初始化全局按键监听器
            keyListener = event -> {
                KeyCode pressedKey = event.getCode();
                // 更新对应控制项的按键配置
                updateKeyCode(controlType, pressedKey);
                // 更新Label显示新按键
                label.setText(labelText + ": " + pressedKey);
                // 重置监听状态
                activeControl = null;
                bindButton.setText("设置按键");
                // 移除全局监听器
                event.getScene().removeEventHandler(KeyEvent.KEY_PRESSED, keyListener);
                // 消费事件,避免传递到其他组件
                event.consume();
            };

            // 为Scene添加全局按键监听器
            parent.getScene().addEventHandler(KeyEvent.KEY_PRESSED, keyListener);
        } else {
            // 取消当前激活的监听
            if (activeControl == controlType) {
                activeControl = null;
                bindButton.setText("设置按键");
                label.setText(labelText + ": " + getCurrentKeyCode(controlType));
                parent.getScene().removeEventHandler(KeyEvent.KEY_PRESSED, keyListener);
            }
        }
    });

    itemRow.getChildren().addAll(label, bindButton);
    parent.getChildren().add(itemRow);
}

5. 辅助方法

// 获取指定控制项的当前按键
private KeyCode getCurrentKeyCode(ControlType controlType) {
    switch (controlType) {
        case MOVE_LEFT: return moveLeftKey;
        case MOVE_RIGHT: return moveRightKey;
        case ROTATE: return rotateKey;
        case SOFT_DROP: return softDropKey;
        case HARD_DROP: return hardDropKey;
        case HOLD: return holdKey;
        default: return KeyCode.UNDEFINED;
    }
}

// 更新指定控制项的按键配置
private void updateKeyCode(ControlType controlType, KeyCode newKey) {
    switch (controlType) {
        case MOVE_LEFT: moveLeftKey = newKey; break;
        case MOVE_RIGHT: moveRightKey = newKey; break;
        case ROTATE: rotateKey = newKey; break;
        case SOFT_DROP: softDropKey = newKey; break;
        case HARD_DROP: hardDropKey = newKey; break;
        case HOLD: holdKey = newKey; break;
    }
}

关键细节说明

  • 使用Scene.addEventHandler注册全局监听器,避免被组件的事件消费逻辑覆盖,确保能捕获所有按键
  • 捕获按键后调用event.consume(),防止事件传递到游戏主逻辑或其他UI组件
  • 每次激活/取消监听时都及时重置状态并移除监听器,避免内存泄漏或重复监听
  • 通过按钮文本切换("设置按键"/"取消监听")直观反馈当前状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 23:45:41