背景說明
本人有一個任務管理工具管理自己每天的工作計劃,按照自己設定的任務按時按量完成工作。
如上圖,這裏展示的一週的任務內容,也就是有7個任務塊的內容水平排列,出現滾動條是必然的。爲了讓我直觀查看一週的內容更加便捷,而不是必須使用細長的滾動條,我想做以下兩點:
1) 左右增加兩個懸浮按鈕,點擊可以將滾動條滾到最左邊或者最右邊(一般我的這些內容只有兩屏,所以點一下右邊按鈕相當於切屏,正好適用)
2) 點擊某個任務塊的日期時,如果該塊內容沒有完全顯示出來,則將滾動條滾到合適位置將內容完全呈現(我在做這一點時,一開始以爲ScrollPane有原生支持,提供了方法將給定的一個Node直接滾動到將其完全顯示,我記得Swing中是有的,但是可惜JavaFX沒有,我纔不得不花了2小時研究滾動條值的計算原理)
注意,我這裏只做了水平方向,豎直方向的原理應該差不多。
一、增加兩個懸浮按鈕
【知識點1】:ScrollPane的水平滾動條值HValue和垂直滾動條值VValue都是Double類型,取值範圍是[0, 1],0表示最左邊或者最上面,1表示最右邊或者最下面
增加兩個懸浮按鈕的原理如下:
- 使用StackPane作爲容器,底下先放置任務塊的內容面板,上面再放置懸浮按鈕所在的面板
- 將上面的按鈕懸浮面板設置不響應鼠標事件,這樣上層的面板不會影響底層的內容面板響應事件
- 在點擊左邊的按鈕時,將ScrollPane的HValue設置爲0,使滾動條滾動到最左邊
- 在點擊右邊的按鈕時,將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
用文字來說就是,最小寬度除以待滾動寬度,或者最大寬度減去可見寬度後再除以待滾動寬度。
根據以上的知識,我們可以使用以下步驟進行處理:
- 當點擊任務塊日期時,傳出一個Change事件,在事件中傳出該任務塊相對於ScrollPane的Bounds
- 獲取ScrollPane的可見寬度,待滾動寬度
- 判斷任務塊的Bounds是屬於超出左邊還是超出右邊,超出左邊使用minX公式,超出右邊使用maxX公式
- 計算出合適的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個知識點對你有用嗎?