1

This question is a follow-up of this one.

The following code draws a Circle in an AnchorPane. The Circle is initialized with centerX=centerY=radius=50. When dragging mouse on the Circle, the Circle moves on the AnchorPane, such that its center coordinates remains at a constant position with respect to cursor position.

import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.DoubleBinding;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class HelloApplication extends Application {

    private final AnchorPane mainPane=new AnchorPane();

    double x=0,y=0;
    double tx =0, ty =0;

    int i=0;

    // create list with an extractor:
    ObservableList<Circle> circles =FXCollections.observableArrayList(circle1 -> new Observable[]{circle1.centerXProperty()});

    Circle circle = new Circle(50,50,50);

    @Override
    public void start(Stage stage) {
        
        // Set up stage for show!
        Scene scene = new Scene(mainPane, 1200, 800);
        stage.setTitle("Hello!");
        stage.setScene(scene);
        stage.show();

        // set fill color.
        circle.setFill(new Color(1, 0, 0, 0.5));

        // set up onMousePressed listener.
        circle.setOnMousePressed(mouseEvent -> {
            x=mouseEvent.getX();
            y=mouseEvent.getY();
        });

        // set up onMouseDragged listener.
        circle.setOnMouseDragged(mouseEvent -> {
            tx=x;
            ty=y;
            x=mouseEvent.getX();
            y=mouseEvent.getY();
            circle.setCenterX(circle.getCenterX()+x-tx);
            circle.setCenterY(circle.getCenterY()+y-ty);
            i++;
        });

        // add rectangle pane children.
        mainPane.getChildren().addAll(circle);

        // add rectangle to rectangles.
        circles.add(circle);

        DoubleBinding centerX = Bindings.createDoubleBinding(
                () -> circles
                        .stream()
                        .mapToDouble(Circle::getCenterX)
                        .min()
                        .orElse(0),
                circles
        );

        centerX.addListener((obs, oldMin, newMin) -> System.out.println("center X changed from " + oldMin + " to " + newMin+" , i = "+i));
    }

    public static void main(String[] args) {
        launch();
    }
}

A custom DoubleBinding centerX tracks the changes in the centerX of the Circle and prints the following message on console:

Min X changed from 50.0 to 58.0 , i = 0

where i is an index [the numbers are arbitrary and may change].

This message printing continues well up to approximately i=7000 and then stops. I would appreciate if someone gives me a hint as to why this happens and how to fix it. Thanks.

Addendum

The reason that I captured the min() of all Circle instances of circles is that I may have more than a Circle and I wish to capture the minimum position of the centerX values of these Circles. I included only one Circle instance in this example for simplicity.

Keen Teen
  • 25
  • 4
  • Tested the same code on `jdk1.8.0_341` and there was no problem, reached 21k. – Miss Chanandler Bong Jun 21 '23 at 13:21
  • @MissChanandlerBong That's a pretty out of date platform to test on. – James_D Jun 21 '23 at 13:24
  • You are running into the "premature garbage collection" problem. See linked Q&A. You can test this by adding `mainPane.setOnMouseClicked(e -> System.gc());`. Then clicking on the pane (not the circle) will cause the binding to stop working immediately. Fix it by making the binding an instance variable (so a reference to it is retained). – James_D Jun 21 '23 at 13:29

0 Answers0