2013-10-28 19 views
6

Tôi nhận được ScrollPane chứa các nút có tiêu điểm.JavaFX: di chuyển so với tiêu điểm với các phím mũi tên

Hành vi mặc định hiện nay là:

  • phím Shift + , , , di chuyển trọng tâm

  • , , , cuộn quan điểm

Tôi muốn nó theo cách khác xung quanh. Tôi làm cách nào để thực hiện việc này hoặc tôi nên bắt đầu từ đâu?


[EDIT] Vâng, đó là một cách tiếp cận mong manh.

Thay vì xáo trộn xung quanh các sự kiện, người ta có thể gây rối xung quanh với KeyBinding giây.

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); 
      } 
     } 
    }); 

Tôi nghĩ, đó có thể là cách rõ ràng hơn, nếu KeyBindings không tĩnh, có thể sửa đổi và công khai hơn.

Trả lời

4

Sử dụng event filter để nắm bắt các sự kiện quan trọng có liên quan và chuyển chúng vào các sự kiện quan trọng khác trước khi sự kiện bắt đầu bong bóng.

Re-mapping phím mặc định là một điều khó khăn đó:

  1. có thể gây nhầm lẫn cho người dùng.
  2. Có thể có các tác dụng phụ không mong muốn (ví dụ: TextFields có thể không hoạt động như bạn mong đợi).

Vì vậy, sử dụng một cách cẩn thận:

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); 
    } 
} 
+0

Đó chính xác là những gì tôi đang tìm kiếm. Một nỗ lực đầu tiên nhanh chóng để tiếp quản mã vào ứng dụng của tôi tuy nhiên dẫn đến một 'StackOverflowError'. Tôi sẽ xem xét điều này vào buổi tối. Cảm ơn! –

+1

Lệnh copyFor là khóa nếu không mã JavaFX sẽ sao chép nội bộ sự kiện để đặt nguồn và đích, vì vậy bạn sẽ kết thúc trong vòng lặp vô tận của sự kiện remapping, chuyển đổi công cụ sửa đổi và tắt, dẫn đến tràn. Hệ thống thử nghiệm của tôi là OS X với Java 8. – jewelsea

+0

Bằng cách nào đó, trong ứng dụng của tôi không có sự kiện nào bị loại bỏ khỏi danh sách 'mappedEvents'. Ví dụ của bạn hoạt động tốt. Tôi không thể tìm ra điều gì làm nên sự khác biệt cho đến nay. –

Các vấn đề liên quan