JavaFX:使用箭头键滚动与焦点遍历 [英] JavaFX: scrolling vs. focus traversal with arrow keys

查看:233
本文介绍了JavaFX:使用箭头键滚动与焦点遍历的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



当前的默认行为是:




  • Shift + 移动焦点

  • kbd>,滚动视图 我想要另一种方式。
    我该如何做到这一点?我应该从哪里开始?




    那么,还有另外一个脆弱的方法。



    不用乱搞事件,可以用 KeyBinding

      scrollPane.skinProperty b $ b @Override 
    public void changed(ObservableValue< ;? extends Skin<>> observable,Skin> oldValue,Skin<> newValue){
    ScrollPaneSkin scrollPaneSkin =(ScrollPaneSkin )scrollPane.getSkin();
    ScrollPaneBehavior scrollPaneBehavior = scrollPaneSkin.getBehavior();
    尝试{
    字段keyBindingsField = BehaviorBase.class.getDeclaredField(keyBindings);
    keyBindingsField。 setAccessible(true);
    List< KeyBinding> keyBindings =(List< KeyBinding>)keyBindings Field.get(scrollPaneBehavior);
    List< KeyBinding> newKeyBindings = new ArrayList<>(); (KeyBinding keyBinding:keyBindings){
    KeyCode code = keyBinding.getCode();
    newKeyBindings.add(code == KeyCode.LEFT || code == KeyCode.RIGHT || code == KeyCode.UP || code == KeyCode.DOWN?keyBinding.shift():keyBinding);
    }
    keyBindingsField.set(scrollPaneBehavior,newKeyBindings);
    } catch(NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e){
    LOGGER.warn(private api changed。,e);
    }
    }
    });

    我认为,如果KeyBindings更加非静态,可修改和公开。

    解决方案

    使用事件过滤器来捕获相关的关键事件,并将它们重新映射到不同的关键事件

    重映射默认键是个棘手的事情:


    1. 可能会混淆用户。

    2. 可能会有意想不到的副作用(例如,TextFields可能无法按预期工作)。 b
      $ b

      请谨慎使用:

        import javafx.application。*; 
      import javafx.event。*;
      导入javafx.scene.Scene;
      import javafx.scene.control。*;
      import javafx.scene.input.KeyEvent;
      import javafx.scene.layout.TilePane;
      import javafx.stage.Stage;

      import java.util。*;

      public class ScrollInterceptor extends Application {
      $ b $ @Override
      public void start(Stage stage){
      ScrollPane scrollPane = new ScrollPane(
      createScrollableContent()
      );

      场景scene = new Scene(
      scrollPane,
      300,200
      );

      remapArrowKeys(scrollPane);

      stage.setScene(scene);
      stage.show();

      hackToScrollToTopLeftCorner(scrollPane);


      private void remapArrowKeys(ScrollPane scrollPane){
      List< KeyEvent> mappedEvents = new ArrayList<>();
      scrollPane.addEventFilter(KeyEvent.ANY,EventHandler< KeyEvent>(){
      @Override
      public void handle(KeyEvent event){
      if(mappedEvents.remove(event) )
      return;

      switch(event.getCode()){
      case UP:
      case DOWN:
      case LEFT:
      case RIGHT:
      KeyEvent newEvent = remap(event);
      mappedEvents.add(newEvent);
      event.consume();
      Event.fireEvent(event.getTarget(),newEvent );
      }
      }

      私钥KeyEvent重映射(KeyEvent事件){
      KeyEvent newEvent = new KeyEvent(
      event.getEventType(),
      event.isShiftDown(),$ b $ event.isControlDown(),$ b event.getCharacter(),
      event.getText(),
      event.getCode(),
      ! b $ b event.isAltDown(),
      event.isMetaDown()
      );

      return newEvent.copyFor(event.getSource(),event.getTarget());
      }
      });

      $ b $ private TilePane createScrollableContent(){
      TilePane tiles = new TilePane();
      tiles.setPrefColumns(10);
      tiles.setHgap(5);
      tiles.setVgap(5);
      for(int i = 0; i <100; i ++){
      Button button = new Button(i +);
      button.setMaxWidth(Double.MAX_VALUE);
      button.setMaxHeight(Double.MAX_VALUE);
      tiles.getChildren()。add(button);
      }
      返回图块;

      $ b private void hackToScrollToTopLeftCorner(final ScrollPane scrollPane){
      Platform.runLater(new Runnable(){
      @Override $ b $ public void run() {
      scrollPane.setHvalue(scrollPane.getHmin());
      scrollPane.setVvalue(0);
      }
      });


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


      I got a ScrollPane containing focusable Nodes.

      The current default behaviour is:

      • Shift + , , , moves the focus

      • , , , scrolls the view

      I want it the other way around. How can I accomplish this or where should I start?


      [EDIT] Well, there is another fragile approach.

      Instead of messing around with the events, one could mess around with the KeyBindings.

          scrollPane.skinProperty().addListener(new ChangeListener<Skin<?>>() {
              @Override
              public void changed(ObservableValue<? extends Skin<?>> observable, Skin<?> oldValue, Skin<?> newValue) {
                  ScrollPaneSkin scrollPaneSkin = (ScrollPaneSkin) scrollPane.getSkin();
                  ScrollPaneBehavior scrollPaneBehavior = scrollPaneSkin.getBehavior();
                  try {
                      Field keyBindingsField = BehaviorBase.class.getDeclaredField("keyBindings");
                      keyBindingsField.setAccessible(true);
                      List<KeyBinding> keyBindings = (List<KeyBinding>) keyBindingsField.get(scrollPaneBehavior);
                      List<KeyBinding> newKeyBindings = new ArrayList<>();
                      for (KeyBinding keyBinding : keyBindings) {
                          KeyCode code = keyBinding.getCode();
                          newKeyBindings.add(code == KeyCode.LEFT || code == KeyCode.RIGHT || code == KeyCode.UP || code == KeyCode.DOWN ? keyBinding.shift() : keyBinding);
                      }
                      keyBindingsField.set(scrollPaneBehavior, newKeyBindings);
                  } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
                      LOGGER.warn("private api changed.", e);
                  }
              }
          });
      

      I think, that could be the cleaner way, if KeyBindings were more non-static, modifyable and public.

      解决方案

      Use an event filter to capture the relevant key events and remap them to different key events before the events start to bubble.

      Re-mapping default keys is a tricky thing which:

      1. Can confuse the user.
      2. May have unexpected side effects (e.g. TextFields may no longer work as you expect).

      So use with care:

      import javafx.application.*;
      import javafx.event.*;
      import javafx.scene.Scene;
      import javafx.scene.control.*;
      import javafx.scene.input.KeyEvent;
      import javafx.scene.layout.TilePane;
      import javafx.stage.Stage;
      
      import java.util.*;
      
      public class ScrollInterceptor extends Application {
      
        @Override
        public void start(Stage stage) {
          ScrollPane scrollPane = new ScrollPane(
            createScrollableContent()
          );
      
          Scene scene = new Scene(
            scrollPane,
            300, 200
          );
      
          remapArrowKeys(scrollPane);
      
          stage.setScene(scene);
          stage.show();
      
          hackToScrollToTopLeftCorner(scrollPane);
        }
      
        private void remapArrowKeys(ScrollPane scrollPane) {
          List<KeyEvent> mappedEvents = new ArrayList<>();
          scrollPane.addEventFilter(KeyEvent.ANY, new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
              if (mappedEvents.remove(event))
                return;
      
              switch (event.getCode()) {
                case UP:
                case DOWN:
                case LEFT:
                case RIGHT:
                  KeyEvent newEvent = remap(event);
                  mappedEvents.add(newEvent);
                  event.consume();
                  Event.fireEvent(event.getTarget(), newEvent);
              }
            }
      
            private KeyEvent remap(KeyEvent event) {
              KeyEvent newEvent = new KeyEvent(
                  event.getEventType(),
                  event.getCharacter(),
                  event.getText(),
                  event.getCode(),
                  !event.isShiftDown(),
                  event.isControlDown(),
                  event.isAltDown(),
                  event.isMetaDown()
              );
      
              return newEvent.copyFor(event.getSource(), event.getTarget());
            }
          });
        }
      
        private TilePane createScrollableContent() {
          TilePane tiles = new TilePane();
          tiles.setPrefColumns(10);
          tiles.setHgap(5);
          tiles.setVgap(5);
          for (int i = 0; i < 100; i++) {
            Button button = new Button(i + "");
            button.setMaxWidth(Double.MAX_VALUE);
            button.setMaxHeight(Double.MAX_VALUE);
            tiles.getChildren().add(button);
          }
          return tiles;
        }
      
        private void hackToScrollToTopLeftCorner(final ScrollPane scrollPane) {
          Platform.runLater(new Runnable() {
            @Override
            public void run() {
              scrollPane.setHvalue(scrollPane.getHmin());
              scrollPane.setVvalue(0);
            }
          });
        }
      
        public static void main(String[] args) {
          launch(args);
        }
      }
      

      这篇关于JavaFX:使用箭头键滚动与焦点遍历的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆