JavaFX ScrollPane滾動到指定位置

背景說明

本人有一個任務管理工具管理自己每天的工作計劃,按照自己設定的任務按時按量完成工作。

week-task

如上圖,這裏展示的一週的任務內容,也就是有7個任務塊的內容水平排列,出現滾動條是必然的。爲了讓我直觀查看一週的內容更加便捷,而不是必須使用細長的滾動條,我想做以下兩點:

1) 左右增加兩個懸浮按鈕,點擊可以將滾動條滾到最左邊或者最右邊(一般我的這些內容只有兩屏,所以點一下右邊按鈕相當於切屏,正好適用)

2) 點擊某個任務塊的日期時,如果該塊內容沒有完全顯示出來,則將滾動條滾到合適位置將內容完全呈現(我在做這一點時,一開始以爲ScrollPane有原生支持,提供了方法將給定的一個Node直接滾動到將其完全顯示,我記得Swing中是有的,但是可惜JavaFX沒有,我纔不得不花了2小時研究滾動條值的計算原理)

注意,我這裏只做了水平方向,豎直方向的原理應該差不多。

一、增加兩個懸浮按鈕

【知識點1】:ScrollPane的水平滾動條值HValue和垂直滾動條值VValue都是Double類型,取值範圍是[0, 1],0表示最左邊或者最上面,1表示最右邊或者最下面

增加兩個懸浮按鈕的原理如下:

  1. 使用StackPane作爲容器,底下先放置任務塊的內容面板,上面再放置懸浮按鈕所在的面板
  2. 將上面的按鈕懸浮面板設置不響應鼠標事件,這樣上層的面板不會影響底層的內容面板響應事件
  3. 在點擊左邊的按鈕時,將ScrollPane的HValue設置爲0,使滾動條滾動到最左邊
  4. 在點擊右邊的按鈕時,將ScrollPane的HValue設置爲1,使滾動條滾動到最右邊

代碼如下:

private void initGlassPane() {
    BorderPane glass = new BorderPane();
    final Button btnLeft = new Button("<");
    btnLeft.setPrefHeight(200);
    btnLeft.getStyleClass().add("transparent");
    btnLeft.setOnAction((ActionEvent event) -> {
        scrollToLeft();
    });
    glass.setLeft(btnLeft);
    BorderPane.setAlignment(btnLeft, Pos.CENTER);
  
    final Button btnRight = new Button(">");
    btnRight.setPrefHeight(200);
    btnRight.getStyleClass().add("transparent");
    btnRight.setOnAction((ActionEvent event) -> {
        scrollToRight();
    });
    glass.setRight(btnRight);
    BorderPane.setAlignment(btnRight, Pos.CENTER);
  
    glass.setPickOnBounds(false);

    btnLeft.setVisible(false);
    scrollPane.hvalueProperty().addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {
        btnLeft.setVisible(newValue.doubleValue() != 0);
        btnRight.setVisible(newValue.doubleValue() != 1);
    });

    this.getChildren().add(glass);
}

public final void scrollToLeft() {
    scrollPane.setHvalue(0);
}

public final void scrollToRight() {
    scrollPane.setHvalue(1);
}

二、點擊任務塊日期完全呈現內容

【知識點2】:scrollPane.getViewportBounds()方法返回Bounds對象,表示ScrollPane中當前顯示區域的大小,這個Bounds對象的Width和Height是正確的,但是其X和Y座標的規則含糊,並不是相對於ScrollPane的位置,也不是相對於屏幕的位置,而且滾動條值變化時該Bounds對象的值不一定變化

【知識點3】:滾動條值與顯示內容位置的關係(滾動條內容Content,顯示內容Viewport):

待滾動寬度ScrollWidth = Content.Width - Viewport.Width

水平滾動條值HValue = Viewport.minX / ScrollWidth ,或

水平滾動條值HValue = (Viewport.maxX - Viewport.Width) / ScrollWidth

用文字來說就是,最小寬度除以待滾動寬度,或者最大寬度減去可見寬度後再除以待滾動寬度。

根據以上的知識,我們可以使用以下步驟進行處理:

  1. 當點擊任務塊日期時,傳出一個Change事件,在事件中傳出該任務塊相對於ScrollPane的Bounds
  2. 獲取ScrollPane的可見寬度,待滾動寬度
  3. 判斷任務塊的Bounds是屬於超出左邊還是超出右邊,超出左邊使用minX公式,超出右邊使用maxX公式
  4. 計算出合適的HValue時,設置給ScrollPane,scrollPane.setHvalue()

代碼如下:

BoundsCheck boundsCheck = (Bounds bounds) -> {
    final Bounds visibleBounds = scrollPane.getViewportBounds();
    double totalWidth = scrollPane.getContent().getBoundsInLocal().getWidth();//總寬度
    double visibleWidth = visibleBounds.getWidth();//可見寬度
    double scrollWidth = totalWidth - visibleWidth;//待滾動寬度
    double hvalue = scrollPane.getHvalue();
    double maxX = visibleWidth + hvalue * scrollWidth;//當前顯示的maxX

    if (bounds.getMinX() < maxX - visibleWidth) {//超出左邊
        double x = bounds.getMinX();
        scrollPane.setHvalue(x / scrollWidth);
    } else if (bounds.getMaxX() > maxX) {//超出右邊
        double x = bounds.getMaxX() - visibleWidth;
        scrollPane.setHvalue(x / scrollWidth);
    }
};

 

到此,我的目標實現了!這3個知識點對你有用嗎?

http://www.alanzeng.cn/2016/03/javafx-scrollpane-value/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章