基于JavaFX(JDK8,无FXML)的俄罗斯方块自定义按键绑定实现问询
JavaFX 8 自定义按键绑定配置实现方案
核心思路
- 为每个控制按钮维护监听状态标记,区分当前是激活监听还是取消监听
- 全局注册
KeyEvent.KEY_PRESSED监听器,仅在有按钮处于激活状态时生效 - 捕获首个按键后更新对应控制项的
KeyCode变量,同时重置所有按钮状态并移除全局监听器
具体实现步骤
- 定义全局变量:存储各控制项的
KeyCode、当前激活的控制项标识、全局按键监听器引用 - 构建设置菜单UI:为每个控制项创建
Label+Button组合,绑定按钮点击事件 - 按钮点击逻辑:切换监听状态,激活时标记控制项并注册全局监听器,取消时重置状态并移除监听器
- 全局按键监听逻辑:捕获按键后更新
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
相关产品推荐
相关产品推荐

