Search code examples
javajavafxposition

Label moves backwards slowly


The code below should move the Label based on the position of the horizontal scroll bar so that the Label appears to remain stationary. This almost works perfectly however when you move the scrollbar to the end the label has moved slightly so it does not look like it is in the same position.

enter image description here

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class LblMoves extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        try {
            VBox images = new VBox();
            images.setPadding(new Insets(0, 0, 0, 0));
            Label posLbl = new Label("0");

            images.getChildren().add(posLbl);

            images.setPrefSize(Integer.MAX_VALUE, 50);
            ScrollPane scrollPane = new ScrollPane(images);
            scrollPane.setStyle("-fx-background: #FFFFFF;");

            scrollPane.hvalueProperty().addListener(new ChangeListener<Number>() {
                public void changed(ObservableValue<? extends Number> ov, Number old_val, Number new_val) {
                    double screenPer = scrollPane.getHvalue() * scrollPane.getWidth();
                    double pos = scrollPane.getHvalue() * images.getWidth();
                    double marg = pos - screenPer;
                    posLbl.setPadding(new Insets(0, 0, 0, marg));
                }
            });

            Scene scene = new Scene(scrollPane, 600, 600);
            primaryStage.setScene(scene);
            primaryStage.setMaximized(true);

            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Solution

  • You use the width of the ScrollPane. However the width used in the calculations for ScrollPane use the viewportBounds.
    Also since by default the position is rounded to full pixels, which causes some movement of the Label (which could be fixed by using translateX instead of the padding).

    InvalidationListener listener = o -> {
        double marg = (images.getWidth() - scrollPane.getViewportBounds().getWidth()) * scrollPane.getHvalue();
        posLbl.setTranslateX(marg);
        // posLbl.setPadding(new Insets(0, 0, 0, marg));
    };
    
    scrollPane.hvalueProperty().addListener(listener);
    scrollPane.viewportBoundsProperty().addListener(listener);
    listener.invalidated(null);