2014-10-07 34 views
2

我試圖在JavaFX中顯示一個非常大的圖像。爲此,我將圖像分成幾個較小的圖像,只加載/顯示我的ScrollPane中可見的部分。JavaFX ScrollPane更新視窗滾動上的邊框

要檢測ScrollPane中可見的區域,我要添加偵聽器到。但是,viewportBounds只在我調整窗口大小時更新,但在滾動滾動條時不會更新。

如果我要滾動我的大圖像,我還需要滾動滾動條以更新viewportBounds。我該怎麼做呢?


我有一些測試代碼如下。點擊Button的作品,但不通過ChangeListener - leftright仍然保持不變,使用ChangeListener

import javafx.application.Application; 
import javafx.beans.value.ChangeListener; 
import javafx.beans.value.ObservableValue; 
import javafx.event.ActionEvent; 
import javafx.event.EventHandler; 
import javafx.geometry.Bounds; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.ScrollPane; 
import javafx.scene.layout.VBox; 
import javafx.scene.paint.Color; 
import javafx.scene.shape.Rectangle; 
import javafx.stage.Stage; 

public class TestClass extends Application { 
    @Override 
    public void start(Stage stage) { 
     VBox vbox = new VBox(); 

     ScrollPane scrollPane = new ScrollPane(new Rectangle(1000, 700, Color.GREEN)); 
     scrollPane.setPrefSize(500, 300); 

     // Using a ChangeListener on viewportBounds doesn't work. 
     scrollPane.viewportBoundsProperty().addListener(new ChangeListener<Bounds>() { 
      @Override 
      public void changed(ObservableValue<? extends Bounds> observable, Bounds oldBounds, Bounds bounds) { 
       int left = -1 * (int) bounds.getMinX(); 
       int right = left + (int) bounds.getWidth(); 
       System.out.println("hval:" + scrollPane.getHvalue() + " left:" + left + " right:" + right); 
      } 
     }); 

     // Neither does this. 
     scrollPane.hvalueProperty().addListener(new ChangeListener<Number>() { 
      @Override 
      public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { 
       Bounds bounds = scrollPane.getViewportBounds(); 
       int left = -1 * (int) bounds.getMinX(); 
       int right = left + (int) bounds.getWidth(); 
       System.out.println("hval:" + scrollPane.getHvalue() + " left:" + left + " right:" + right); 
      } 
     }); 

     // Clicking the button works. 
     Button printButton = new Button("Print"); 

     printButton.setOnAction(new EventHandler<ActionEvent>() { 
      @Override 
      public void handle(ActionEvent event) { 
       Bounds bounds = scrollPane.getViewportBounds(); 
       int left = -1 * (int) bounds.getMinX(); 
       int right = left + (int) bounds.getWidth(); 
       System.out.println("hval:" + scrollPane.getHvalue() + " left:" + left + " right:" + right); 
       event.consume(); 
      } 
     }); 

     vbox.getChildren().addAll(scrollPane, printButton); 

     Scene scene = new Scene(vbox, 640, 480); 
     stage.setScene(scene); 

     stage.show(); 
    } 

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

編輯更新測試代碼。

回答

4

viewportBounds只是視口的邊界,即可見的可滾動內容部分的邊界。這些在滾動時不會改變(但隨着大小的改變,通常會隨着您調整窗口大小而改變)。

要響應在視口內移動的滾動內容,您需要注意ScrollPanehvaluePropertyvvalueProperty。您可以使用相同的更改偵聽器進行微小的改進的參數類型:

import javafx.application.Application; 
import javafx.beans.value.ChangeListener; 
import javafx.beans.value.ObservableValue; 
import javafx.geometry.Bounds; 
import javafx.scene.Scene; 
import javafx.scene.control.ScrollPane; 
import javafx.scene.paint.Color; 
import javafx.scene.shape.Rectangle; 
import javafx.stage.Stage; 

public class TestClass extends Application { 
    @Override 
    public void start(Stage stage) { 
     ScrollPane scrollPane = new ScrollPane(new Rectangle(1000, 700, Color.GREEN)); 
     scrollPane.setPrefSize(500, 300); 

     ChangeListener<Object> changeListener = new ChangeListener<Object>() { 
      @Override 
      public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) { 
       Bounds bounds = scrollPane.getViewportBounds(); 
       int left = -1 * (int) bounds.getMinX(); 
       int right = left + (int) bounds.getWidth(); 
       System.out.println("hval:" + scrollPane.getHvalue() + " left:" + left + " right:" + right); 
      } 
     }; 
     scrollPane.viewportBoundsProperty().addListener(changeListener); 
     scrollPane.hvalueProperty().addListener(changeListener); 
     scrollPane.vvalueProperty().addListener(changeListener); 

     Scene scene = new Scene(scrollPane, 640, 480); 
     stage.setScene(scene); 

     stage.show(); 
    } 

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

注意,當您滾動,(當然)視口不其父移動;滾動窗格內容的可見部分發生變化。我不能用簡單的方法計算內容的可見部分:你只需要做一些數學運算。

(注:有可能是一個更簡單的方法,但我不能看到它)

API docs for ScrollPane狀態

滾動面板允許應用程序設置當前,最小和 用於定位水平和垂直方向上的內容的最大值。這些值按比例映射到包含節點的layoutBounds。

所以,你必須來解釋了一下,但它意味着

(hvalue - hmin)/(hmax - hmin) = hoffset/freeHspace 

其中hminhvaluehmax是滾動窗格中的屬性值,hoffset是由內容水平移動量,而freeHspace是內容可以移動的總水平量。這是

freeHspace = contentWidth - viewportWidth 

(顯然,如果contentWidth < = viewportWidth,沒有水平滾動是可能的,hoffset必然是零。)

所以,如果你想要的水平偏移你有

hoffset = Math.max(0, contentWidth - viewportWidth) * (hvalue - hmin)/(hmax - hmin) 

對於垂直偏移量也有類似的公式。

所以,你可以替換更改偵聽以上

ChangeListener<Object> changeListener = new ChangeListener<Object>() { 
     @Override 
     public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) { 
      double hmin = scrollPane.getHmin(); 
      double hmax = scrollPane.getHmax(); 
      double hvalue = scrollPane.getHvalue(); 
      double contentWidth = content.getLayoutBounds().getWidth(); 
      double viewportWidth = scrollPane.getViewportBounds().getWidth(); 

      double hoffset = 
       Math.max(0, contentWidth - viewportWidth) * (hvalue - hmin)/(hmax - hmin); 

      double vmin = scrollPane.getVmin(); 
      double vmax = scrollPane.getVmax(); 
      double vvalue = scrollPane.getVvalue(); 
      double contentHeight = content.getLayoutBounds().getHeight(); 
      double viewportHeight = scrollPane.getViewportBounds().getHeight(); 

      double voffset = 
       Math.max(0, contentHeight - viewportHeight) * (vvalue - vmin)/(vmax - vmin); 

      System.out.printf("Offset: [%.1f, %.1f] width: %.1f height: %.1f %n", 
        hoffset, voffset, viewportWidth, viewportHeight); 
     } 
    }; 
+0

我可以響應滾動條的變化是這樣的,但它並不完全符合我要找的。我需要能夠在'ChangeListener'(和'top'和'botttom')中計算'left'和'right'。當我滾動滾動條時,「hval」打印正確,但「left」和「right」保持其初始值。 – sorbet 2014-10-07 21:48:15

+0

當然,你只是從視口範圍計算它們,這是視口在其父代中的範圍。當你滾動時,這些不會改變。我更新了答案以顯示如何計算內容的可見部分。 – 2014-10-07 23:27:35

+0

但是爲什麼'viewportBounds'只有在調整大小時纔會改變?在我的[測試代碼](http://pastie.org/private/zsjaojwtytphephoxp3rg)中,我的'viewport'的'getMinX()'和'getMinY()'會返回更新後的座標,但只能在調整大小之後。 – sorbet 2014-10-08 21:43:40