JavaFX ScrollBar error
I have a simple JavaFX application with Canvas and ScrollBar. Canvas acts like a virtualized canvas whose content can be scrolled using a ScrollBar.
The canvas width is 500 pixels, but the logical canvas width is 1000 pixels. I set the ScrollBar max to 1000 and the ScrollBar value to 500. The problem is that when the ScrollBar scrolls all the way to the right, the ScrollBar value is 1000, not 500. Logically, when the ScrollBar scrolls all the way to the right, there must be some way to determine the actual scrollable offset and this is not possible. Please tell me how you can get the required amount of scrolling. Thanks you
Here's an example scrolling all the way to the left. The scrollbar looks good. Its visible width is 50% of the window size.
Here's an example scrolling halfway to the right. The problem is clear here. The canvas scrolls 500px to the right, but if the canvas scrolls halfway correctly, it will only scroll 250px.
Here's an example, scrolled all the way to the right.
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.ScrollBar;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.stage.Stage;
public class Sc extends Application {
public ScrollBar scrollBar;
double scrolled;
Canvas canvas;
@Override
public void start(Stage stage) {
VBox vbox = new VBox();
Scene scene = new Scene(vbox);
stage.setScene(scene);
canvas = new Canvas(500, 100);
scrollBar = new ScrollBar();
scrollBar.setMin(0);
scrollBar.setMax(1000);
scrollBar.setVisibleAmount(500);
vbox.getChildren().addAll(canvas, scrollBar);
draw();
scrollBar.valueProperty().addListener(new ChangeListener<Number>() {
public void changed(ObservableValue<? extends Number> ov,
Number old_val, Number new_val) {
scrolled = new_val.doubleValue();
draw();
}
});
stage.show();
}
private void draw()
{
GraphicsContext graphics = canvas.getGraphicsContext2D();
graphics.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
Stop[] stops = new Stop[] { new Stop(0, Color.BLACK), new Stop(1, Color.RED)};
LinearGradient lg = new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE, stops);
graphics.setFill(lg);
graphics.fillRect(0-scrolled, 30, 1000, 40);
}
public static void main(String[] args) {
launch(args);
}
}
source to share
Think of it this way:
min
, max
and value
are the boolean values โโof the scrollbar. value
is between min
and max
and is equal min
if and only if the scrollbar scroll is fully scrolled to the left (or top, for a vertical scrollbar). It is equal to max if and only if the scrollbar is scrolled as far to the right (or bottom) as possible.
A property visibleAmount
is simply a visual property that determines the size of the thumb. This relationship is similar to
thumbSize / trackSize = visibleAmount / (max - min)
visibleAmount
will affect the relationship between actual pixels and "logical units"; however, this does not change the fact that it value
can vary over the entire range [min, max]
.
Thus, the only change you need to make to your code is to set the maximum value of the scrollbar to the maximum value that can be scrolled (not the size of the image you are drawing), which is 1000 - canvas.getWidth()
:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.ScrollBar;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.stage.Stage;
public class ScrollBarExample extends Application {
private final static double TOTAL_WIDTH = 1000 ;
@Override
public void start(Stage stage) {
VBox vbox = new VBox();
Scene scene = new Scene(vbox, 500, 130);
stage.setScene(scene);
Canvas canvas = new Canvas(500, 100);
ScrollBar scrollBar = new ScrollBar();
scrollBar.setMin(0);
scrollBar.setMax(TOTAL_WIDTH - canvas.getWidth());
scrollBar.setVisibleAmount(scrollBar.getMax() * canvas.getWidth() / TOTAL_WIDTH);
vbox.getChildren().addAll(canvas, scrollBar);
draw(canvas, scrollBar.getValue());
scrollBar.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> ov,
Number old_val, Number new_val) {
draw(canvas, scrollBar.getValue());
}
});
stage.show();
}
private void draw(Canvas canvas, double scrollAmount)
{
GraphicsContext graphics = canvas.getGraphicsContext2D();
graphics.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
Stop[] stops = new Stop[] { new Stop(0, Color.BLACK), new Stop(1, Color.RED)};
LinearGradient lg = new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE, stops);
graphics.setFill(lg);
graphics.fillRect(0-scrollAmount, 30, TOTAL_WIDTH, 40);
}
public static void main(String[] args) {
launch(args);
}
}
source to share