12

我正在尝试使 JavaFX Mnemonic 工作。我在场景中有一些按钮,我想要实现的是通过按 Ctrl+S 来触发这个按钮事件。这是一个代码片段:

@FXML
public Button btnFirst;

btnFirst.getScene().addMnemonic(new Mnemonic(btnFirst, 
            new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN)));

Button 的 mnemonicParsing 为 false。(好吧,在尝试完成这项工作时,我尝试将其设置为 true,但没有结果)。JavaFX 文档指出,当一个 Mnemonic 被注册到一个 Scene 上,并且 KeyCombination 到达未消费的 Scene 时,那么目标 Node 将被发送一个 ActionEvent。但这不起作用,可能,我做错了......

我可以使用标准按钮的助记符(通过将 mnemonicParsing 设置为 true 并在“F”字母前加上下划线字符)。但是这种方式用户必须使用 Alt 键,这会在带有菜单栏的浏览器上带来一些奇怪的行为(如果应用程序嵌入到网页中,而不是在按下 Alt+S 触发按钮事件后激活浏览器的菜单)。此外,标准方式使得无法制作快捷键如 Ctrl+Shift+F3 等。

那么,是否有某种方法可以使这项工作?

4

1 回答 1

23

对于您的用例,我认为您实际上想要使用加速器而不是助记符。

button.getScene().getAccelerators().put(
  new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN), 
  new Runnable() {
    @Override public void run() {
      button.fire();
    }
  }
);

在大多数情况下,建议您使用 KeyCombination.SHORTCUT_DOWN 作为修饰符说明符,如上面的代码所示。KeyCombination文档中有一个很好的解释:

快捷键修饰符用于表示主机平台键盘快捷键中常用的修饰键。例如,Windows 上的控制和 Mac 上的元(命令键)。通过使用快捷键修饰符,开发人员可以创建独立于平台的快捷键。因此,“Shortcut+C”组合键在内部处理为 Windows 上的“Ctrl+C”和 Mac 上的“Meta+C”。

如果您想专门编写代码以仅处理 Ctrl+S 组合键,您可以使用:

new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN)

这是一个可执行的示例:

import javafx.animation.*;
import javafx.application.Application;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.input.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class SaveMe extends Application {
  @Override public void start(final Stage stage) throws Exception {
    final Label response = new Label();
    final ImageView imageView = new ImageView(
      new Image("http://icons.iconarchive.com/icons/gianni-polito/colobrush/128/software-emule-icon.png")
    );
    final Button button = new Button("Save Me", imageView);
    button.setStyle("-fx-base: burlywood;");
    button.setContentDisplay(ContentDisplay.TOP);
    displayFlashMessageOnAction(button, response, "You have been saved!");

    layoutScene(button, response, stage);
    stage.show();

    setSaveAccelerator(button);
  }

  // sets the save accelerator for a button to the Ctrl+S key combination.
  private void setSaveAccelerator(final Button button) {
    Scene scene = button.getScene();
    if (scene == null) {
      throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
    }

    scene.getAccelerators().put(
      new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN), 
      new Runnable() {
        @Override public void run() {
          fireButton(button);
        }
      }
    );
  }

  // fires a button from code, providing visual feedback that the button is firing.
  private void fireButton(final Button button) {
    button.arm();
    PauseTransition pt = new PauseTransition(Duration.millis(300));
    pt.setOnFinished(new EventHandler<ActionEvent>() {
      @Override public void handle(ActionEvent event) {
        button.fire();
        button.disarm();
      }
    });
    pt.play();
  }

  // displays a temporary message in a label when a button is pressed, 
  // and gradually fades the label away after the message has been displayed.
  private void displayFlashMessageOnAction(final Button button, final Label label, final String message) {
    final FadeTransition ft = new FadeTransition(Duration.seconds(3), label);
    ft.setInterpolator(Interpolator.EASE_BOTH);
    ft.setFromValue(1);
    ft.setToValue(0);
    button.setOnAction(new EventHandler<ActionEvent>() {
      @Override public void handle(ActionEvent event) {
        label.setText(message);
        label.setStyle("-fx-text-fill: forestgreen;");
        ft.playFromStart();
      }
    });
  }

  private void layoutScene(final Button button, final Label response, final Stage stage) {
    final VBox layout = new VBox(10);
    layout.setPrefWidth(300);
    layout.setAlignment(Pos.CENTER);
    layout.getChildren().addAll(button, response);
    layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 20; -fx-font-size: 20;");
    stage.setScene(new Scene(layout));
  }

  public static void main(String[] args) { launch(args); }
}
// icon license: (creative commons with attribution) http://creativecommons.org/licenses/by-nc-nd/3.0/
// icon artist attribution page: (eponas-deeway) http://eponas-deeway.deviantart.com/gallery/#/d1s7uih

样本输出:

示例程序输出

2020 年 1 月更新,对多个控件使用相同的加速器

当前和以前的实现(JavaFX 13 和更早版本)中的加速器的一个警告是,您不能开箱即用地定义相同的加速键组合以用于单个应用程序中的多个菜单或控件。

有关更多信息,请参阅:

链接的问题报告包括一个解决方法,您可以使用它来允许您在应用程序的多个位置定义和使用相同的加速器(例如,在不同上下文菜单中的两个不同菜单项上)。

请注意,这仅适用于尝试在应用程序中的多个位置使用相同的加速器,如果您不需要尝试这样做,则可以忽略此信息。

于 2012-10-03T18:38:42.647 回答