如何覆盖 ScrollPane 的默认箭头键行为

问题描述

我希望 ScrollPane 中的箭头键以认方式以外的方式工作。在 Keypressed 上的 ScrollPane 处理程序中使用箭头键不会阻止 ScrollPane 处理它们。有什么有效的方法吗?

在示例程序中,

  • 键入 h 或向左箭头以按预期将矩形在网格上向左移动。
  • 键入 l 或向右箭头以按预期将矩形在网格上向右移动。

Start

图 1. 开始

Start,then h key

图 2. 开始,然后 h 键(矩形在网格上向左移动)

Start,then left-arrow key

图。 3.开始,然后向左箭头键(rect在网格上向左移动,ScrollPane滑块向左移动了不需要的移动)

注意事项:

  • KeyClickedkeyreleased 显然无关。
  • 一种解决方法可能是请求将注意力集中在 rect 上,但这会带来不必要的并发症。
  • 另一种解决方法是关注包含的 Pane
  • 我喜欢操作系统突出显示 ScrollPane 的边框,指示键盘焦点。
  • 专注于包含的 Pane 可防止键盘焦点突出显示
  • 覆盖箭头键的ScrollPane 行为对我的应用来说不是问题。
  • StackOverflow 上的相关问题并不完全相同。
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class ScrollPaneArrowKeys extends Application {

  public static void main(String[] args) { launch(args); }

  static Rectangle  rect;
  static ScrollPane sp;

  @Override
  public void start(Stage stage) {
        rect  = new Rectangle (100,50);
    var pane  = new Pane      (rect);
        sp    = new ScrollPane(pane);
    var scene = new Scene     (sp);
    rect.setFill(Color.PALEGREEN);
    pane.setMinWidth (600);
    pane.setMinHeight(150);
    pane.setStyle(grid);
    sp.setMinWidth (300);
    sp.setMinHeight(170);
    sp.setHvalue(0.5);
    sp.setonKeypressed(ScrollPaneArrowKeys::onKeypressed);
    sp.requestFocus();
    reset();
    stage.setScene(scene);
    stage.show();
  }

  private static void onKeypressed(KeyEvent e) {
    switch (e.getCode()) {
      case H:
      case LEFT:   rect.setX(rect.getX() - 10); break;
      case L:
      case RIGHT:  rect.setX(rect.getX() + 10); break;
      case ESCAPE: reset();                     break;
      default:                                  break;
    }
    e.consume();
  }

  private static void reset() {
    rect.setX(250);
    rect.setY( 50);
    sp.setHvalue(0.5);
  }

  String grid = """
      -fx-background-color: white,linear-gradient(from 0px 0px to 10px  0px,repeat,#d8f0f8 6.25%,transparent 6.25%),linear-gradient(from 0px 0px to 50px  0px,#b0e0e8 1.25%,transparent 1.25%),linear-gradient(from 0px 0px to  0px 10px,linear-gradient(from 0px 0px to  0px 50px,transparent 1.25%);
      """;
}

解决方法

如果要覆盖滚动窗格的默认键处理,可以通过注册事件过滤器(而不是添加另一个事件处理程序)来实现。您可以在 e 中阅读有关此内容的信息。 G。这篇文章:JavaFX: What is the difference between EventHandler and EventFilter?

考虑到用户体验,您可能希望添加另一种滚动可能性(例如 CTRL + LEFT 等)。这是一个工作示例(大部分取自您的代码):

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;


public class ScrollPaneArrowKeys extends Application {

    private final Rectangle rect = new Rectangle(100,50);
    private final Pane pane = new Pane(rect);
    private final ScrollPane sp = new ScrollPane(pane);
    private final Scene scene = new Scene(sp);

    @Override
    public void start(Stage stage) {

        // Register an Event Filter to override the default behaviour:
        sp.addEventFilter(KeyEvent.KEY_PRESSED,e -> {
            e.consume(); // prevent default key handling
            // Allow the user to scroll horizontally with the control key:
            if (e.isControlDown())
                switch (e.getCode()) {
                    case LEFT -> sp.setHvalue(sp.getHvalue() - .1);
                    case RIGHT -> sp.setHvalue(sp.getHvalue() + .1);
                }
            else
                // Move they rectangle only:
                switch (e.getCode()) {
                    case H,LEFT -> rect.setX(rect.getX() - 10);
                    case L,RIGHT -> rect.setX(rect.getX() + 10);
                    case ESCAPE -> resetRectPosition();
                }
        });

        initStyling();
        resetRectPosition();
        stage.setScene(scene);
        stage.show();
        sp.requestFocus();
    }

    private void initStyling() {
        rect.setFill(Color.PALEGREEN);

        pane.setMinWidth(600);
        pane.setMinHeight(150);
        pane.setStyle("""
                -fx-background-color: white,linear-gradient(from 0px 0px to 10px  0px,repeat,#d8f0f8 6.25%,transparent 6.25%),linear-gradient(from 0px 0px to 50px  0px,#b0e0e8 1.25%,transparent 1.25%),linear-gradient(from 0px 0px to  0px 10px,linear-gradient(from 0px 0px to  0px 50px,transparent 1.25%);
                """);

        sp.setMinWidth(300);
        sp.setMinHeight(170);
        sp.setHvalue(0.5);
    }

    private void resetRectPosition() {
        rect.setX(250);
        rect.setY(50);
        sp.setHvalue(0.5);
    }

    public static void main(String[] args) {
        launch(args);
    }
}