2012-11-05 15 views
9

Mam aplikację, która wymaga powiększenia wewnątrz ScrollPane, ale z moim obecnym podejściem wciąż mam do czynienia z 2 wyzwaniami. W celu odtworzenia problemu napisałem małą aplikację ZoomApp, w której znajdziesz kod pod spodem. Jego "ograniczona funkcjonalność pozwala na powiększanie i pomniejszanie (za pomocą Ctrl + kółka myszy) w niektórych dowolnych kształtach. Gdy powiększona zawartość wykracza poza granice okna, paski przewijania powinny być widoczne.Powiększanie JavaFx: ScrollEvent jest zużywany, gdy rozmiar zawartości przekracza rzutnię ScrollPane

Wyzwanie 1. Gdy paski przewijania pojawia się jako konsekwencja innerGroup skaluje się w wielkości, ScrollEvent już osiągnąć mój ZoomHandler. Zamiast tego zaczynamy przewijanie w dół okna, aż osiągnie dno, gdy powiększanie ponownie działa zgodnie z oczekiwaniami. Pomyślałem, że może być różnica, ale nie. Jak unikać tego niepożądanego zachowania?

Zadanie 2. Jak bym go o centrum innerGroup wewnątrz ScrollPane, bez uciekania się do malowania piksela w lewym górnym rogu innerGroup z pożądanym delty do kwadratów?

Jako zdarzenie, zgodnie z JavaDoc dla ScrollPane: "Jeśli aplikacja chce, aby przewijanie było oparte na wizualnych granicach węzła (dla skalowanych treści itp.), Muszą zawinąć węzeł przewijania w Grupa ". To jest powód, dla którego mam wewnątrz ScrollPane wewnątrz obiektuizewnętrzną gatunek.

Wszelkie sugestie, które prowadzą mnie do rozwiązania, są bardzo cenione przez początkującego programistę JavaFX.

import javafx.application.Application; 
import javafx.event.EventHandler; 
import javafx.scene.Group; 
import javafx.scene.Node; 
import javafx.scene.Scene; 
import javafx.scene.SceneBuilder; 
import javafx.scene.control.ScrollPane; 
import javafx.scene.input.ScrollEvent; 
import javafx.scene.layout.StackPane; 
import javafx.scene.paint.Color; 
import javafx.scene.shape.Rectangle; 
import javafx.stage.Stage; 

/** 
* Demo of a challenge I have with zooming inside a {@code ScrollPane}. 
* <br> 
* I am running JavaFx 2.2 on a Mac. {@code java -version} yields: 
* <pre> 
* java version "1.7.0_09" 
* Java(TM) SE Runtime Environment (build 1.7.0_09-b05) 
* Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode) 
* </pre> 
* 6 rectangles are drawn, and can be zoomed in and out using either 
* <pre> 
* Ctrl + Mouse Wheel 
* or Ctrl + 2 fingers on the pad. 
* </pre> 
* It reproduces a problem I experience inside an application I am writing. 
* If you magnify to {@link #MAX_SCALE}, an interesting problem occurs when you try to zoom back to {@link #MIN_SCALE}. In the beginning 
* you will see that the {@code scrollPane} scrolls and consumes the {@code ScrollEvent} until we have scrolled to the bottom of the window. 
* Once the bottom of the window is reached, it behaves as expected (or at least as I was expecting). 
* 
* @author Skjalg Bjørndal 
* @since 2012.11.05 
*/ 
public class ZoomApp extends Application { 

    private static final int WINDOW_WIDTH = 800; 
    private static final int WINDOW_HEIGHT = 600; 

    private static final double MAX_SCALE = 2.5d; 
    private static final double MIN_SCALE = .5d; 

    private class ZoomHandler implements EventHandler<ScrollEvent> { 

     private Node nodeToZoom; 

     private ZoomHandler(Node nodeToZoom) { 
      this.nodeToZoom = nodeToZoom; 
     } 

     @Override 
     public void handle(ScrollEvent scrollEvent) { 
      if (scrollEvent.isControlDown()) { 
       final double scale = calculateScale(scrollEvent); 
       nodeToZoom.setScaleX(scale); 
       nodeToZoom.setScaleY(scale); 
       scrollEvent.consume(); 
      } 
     } 

     private double calculateScale(ScrollEvent scrollEvent) { 
      double scale = nodeToZoom.getScaleX() + scrollEvent.getDeltaY()/100; 

      if (scale <= MIN_SCALE) { 
       scale = MIN_SCALE; 
      } else if (scale >= MAX_SCALE) { 
       scale = MAX_SCALE; 
      } 
      return scale; 
     } 
    } 

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

    @Override 
    public void start(Stage stage) throws Exception { 

     final Group innerGroup = createSixRectangles(); 
     final Group outerGroup = new Group(innerGroup); 

     final ScrollPane scrollPane = new ScrollPane(); 
     scrollPane.setContent(outerGroup); 
     scrollPane.setOnScroll(new ZoomHandler(innerGroup)); 

     StackPane stackPane = new StackPane(); 
     stackPane.getChildren().add(scrollPane); 

     Scene scene = SceneBuilder.create() 
       .width(WINDOW_WIDTH) 
       .height(WINDOW_HEIGHT) 
       .root(stackPane) 
       .build(); 

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

    private Group createSixRectangles() { 
     return new Group(
       createRectangle(0, 0), createRectangle(110, 0), createRectangle(220, 0), 
       createRectangle(0, 110), createRectangle(110, 110), createRectangle(220, 110), 
       createRectangle(0, 220), createRectangle(110, 220), createRectangle(220, 220) 
     ); 
    } 

    private Rectangle createRectangle(int x, int y) { 
     Rectangle rectangle = new Rectangle(x, y, 100, 100); 
     rectangle.setStroke(Color.ORANGERED); 
     rectangle.setFill(Color.ORANGE); 
     rectangle.setStrokeWidth(3d); 
     return rectangle; 
    } 
} 

Odpowiedz

12

OK, więc w końcu znalazłem rozwiązanie mojego problemu.

Poprzez zastąpienie jedynie linię

scrollPane.setOnScroll(new ZoomHandler(innerGroup)); 

z

scrollPane.addEventFilter(ScrollEvent.ANY, new ZoomHandler(innerGroup)); 

teraz działa zgodnie z oczekiwaniami. Nie potrzeba żadnego mistycznego prostokąta ani innych hacków.

Następne pytanie brzmi: dlaczego?Zgodnie z this excellent article on Processing Events,

Filtr zdarzeń jest wykonywany podczas fazy przechwytywania zdarzeń.

podczas

obsługi zdarzeń jest wykonywany w fazie propagacji zdarzeń.

Zakładam, że to właśnie robi różnicę.

+0

Nice Skjalg, powinienem czytać instrukcje więcej :) Lub może po prostu zapytać stackoverflow ... to brzmi łatwiej. Dzięki za opublikowanie tego dziwactwa. – Bhupen

2

Następujące obejścia wydaje się dawać lepsze rezultaty:

  1. Ustaw onscroll zdarzeń na grupy zewnętrznej ukraść zdarzenie z panelu przewijania.
  2. Dodaj nieprzezroczysty prostokąt, który obejmuje cały ekran, aby nie przegapić zdarzenia przewijania. Najwyraźniej możesz przegapić przewijanie, jeśli nie uderzysz w kształt.

    Rectangle opaque = new Rectangle(0,0,WINDOW_WIDTH,WINDOW_HEIGHT); 
    opaque.setOpacity(0); 
    outerGroup.getChildren().add(opaque); 
    outerGroup.setOnScroll(new ZoomHandler(innerGroup)); 
    
+0

+1 @Bhupendra obejście problemu . Nadal uważam, że powinno być możliwe naprawienie tego bez dodawania tego dodatkowego prostokąta. Intuicyjnie wydaje mi się to następujące: "Prawdopodobnie możesz przegapić przewijanie, jeśli nie uderzysz w kształt_", ale może być jaśniejsze, ponieważ lepiej poznałem obsługę zdarzeń w JavaFx2. Oprócz zmian należy również wziąć pod uwagę, że okno zmieni rozmiar, podobnie jak "nieprzejrzysty" prostokąt. – Skjalg

+0

Zastąpiłem wewnętrzną "Grupę" okienkiem "Pane", a następnie nie przeoczyłem zdarzenia przewijania między kształtami, ale zdarzenie jest teraz ograniczone tylko do rozmiaru wewnętrznej grupy. Daj mi znać, co znajdziesz. Dzięki. – Bhupen

+0

Prześlę z powrotem, które podejście podjąłem @Bhupendra. Teraz jestem w innej części kodu, więc może to potrwać kilka dni. Dzięki za zainteresowanie. – Skjalg

0

W moim przypadku mam zaktualizowany następujący wiersz
if (scrollEvent.isControlDown()) {
z
if (!scrollEvent.isConsumed()) { .... wraz ze zmianą Wysłany .... :)