2014-10-07 33 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