Problems getting the width and height of a pane from a class object - Javafx

John Conner picture John Conner · Jul 25, 2014 · Viewed 10.2k times · Source

Ok so I understand the binding principle on resizing objects in a pane as it is being resized, this problem is a bit different. I am confused, I am trying to create a class that extends Pane that creates in my main a line that has it's startX and startY coordinates bound to the center of a Pane. The problem is that when using getWidth() / 2 or getHeight() /2 the coordinate are being placed somewhere up and to the left of the starting coordinates (0, 0) when I press my arrow key's which when pressed creates another line that is drawn in the given direction pressed and starting from the end of the last line drawn.

Like I said when I use getWidth() / 2 and getHeight / 2 as the startX and startY coordinates of my new line, in return the line gets placed in a negative coordinate, placing it off the screen above and to the left of the starting (0, 0) coordinates of the pane.

Below is part of my code which contains the default constructor which I am having the problem with, on the non-default constructor I give the ability to manually enter the starting coordinates and when I do this, the line is placed exactly where I want it.

public class LineDrawingObject extends Pane {
    // ArrayList to store the Line Object's
    ArrayList<Line> lines = new ArrayList<>();
    Line line;
    private Color lineColor;
    private double lineLength;
    private int lineCount = 0;
    private double startX;
    private double startY;
    private double endX;
    private double endY;

    /** Default Constructor */
    public LineDrawingObject() {
        this.lineLength = 20;
        line = new Line(this.getWidth() / 2, this.getHeight() / 2,
            (this.getWidth() / 2), (this.getHeight() / 2) - this.lineLength);
        this.lineColor = Color.BLACK;
        line.setStroke(this.lineColor);
        this.lineCount++;
        this.lines.add(line);
        getChildren().add(line);
    }

Edit: Figured I might need to add more information

also I wanted to add that my pane size is set in new Scene(pane, 250, 250) so the center coordinates would be (125, 125)....Would using the getWidth and getHeight methods on the pane return an invalid size if it hasn't yet been drawn? I tried setting the preferred size within my start method but it didn't seem to work. If that is the case how would I remedy this problem?

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

/**
 * Created by John on 7/24/2014.
 */
public class DrawLines extends Application {
    @Override // Override the start method in the Application class
    public void start(Stage primaryStage) {
        // Create a pane
        Pane pane = new Pane();

        // Create object to draw lines upon KeyEvent
        LineDrawingObject lineDrawingObject = new LineDrawingObject(20, Color.BLACK,
            pane.getWidth() / 2, pane.getWidth() / 2);
        pane.getChildren().add(lineDrawingObject);
        lineDrawingObject.setOnKeyPressed(e -> {
            lineDrawingObject.paintLine(e.getCode());
        });

        // Create a scene and place it in the pane
        Scene scene = new Scene(pane, 250, 250);
        primaryStage.setTitle("DrawLines"); // Set the stage title
        primaryStage.setScene(scene); // Place the scene in the stage
        primaryStage.show(); // Display the stage

        // Allow object to receive key input
        lineDrawingObject.requestFocus();
    }
}

and here is the LineDrawing Object:

import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;

import java.util.ArrayList;

/** This object will draw lines inside of a Pane when an arrow key is
 *  pressed and will draw it in that direction from the current line */
public class LineDrawingObject extends Pane {
    // ArrayList to store the Line Object's
    ArrayList<Line> lines = new ArrayList<>();
    Line line;
    private Color lineColor;
    private double lineLength;
    private int lineCount = 0;
    private double startX;
    private double startY;
    private double endX;
    private double endY;

    /** Default Constructor */
    public LineDrawingObject() {
        this.lineLength = 20;
        line = new Line(this.getWidth() / 2, this.getHeight() / 2,
            (this.getWidth() / 2), (this.getHeight() / 2) - this.lineLength);
        this.lineColor = Color.BLACK;
        line.setStroke(this.lineColor);
        this.lineCount++;
        this.lines.add(line);
        getChildren().add(line);
    }

    /** Secondary Constructor, allows you to control the line length and color */
    public LineDrawingObject(double lineLength, Color lineColor, double startX, double startY) {
        this.lineLength = lineLength;
        line = new Line(startX, startY,
                startX, startY - this.lineLength);
        this.lineColor = lineColor;
        line.setStroke(this.lineColor);
        this.lineCount++;
        this.lines.add(line);
        getChildren().add(line);
    }

    public ArrayList<Line> getLines() {
        return lines;
    }

    public void setLines(ArrayList<Line> lines) {
        this.lines = lines;
    }

    public Line getLine() {
        return this.line;
    }

    public void setLine(Line line) {
        this.line = line;
    }

    public Color getLineColor() {
        return this.lineColor;
    }

    public void setLineColor(Color lineColor) {
        this.lineColor = lineColor;
    }

    public double getLineLength() {
        return this.lineLength;
    }

    public void setLineLength(double lineLength) {
        this.lineLength = lineLength;
    }

    public int getLineCount() {
        return this.lineCount;
    }

    public void setLineCount(int lineCount) {
        this.lineCount = lineCount;
    }

    public double getStartX() {
        return this.startX;
    }

    public void setStartX(double startX) {
        this.startX = startX;
    }

    public double getStartY() {
        return this.startY;
    }

    public void setStartY(double startY) {
        this.startY = startY;
    }

    public double getEndX() {
        return this.endX;
    }

    public void setEndX(double endX) {
        this.endX = endX;
    }

    public double getEndY() {
        return this.endY;
    }

    public void setEndY(double endY) {
        this.endY = endY;
    }

    public void paintLine(KeyCode keyCode) {
        // Set line start coordinates to the end of the last line
        setStartX(line.getEndX());
        setStartY(line.getEndY());

        // Set line end coordinates
        switch (keyCode) {
            case UP: goUp(); break;
            case LEFT: goLeft(); break;
            case DOWN: goDown(); break;
            case RIGHT: goRight(); break;
        }

        // Create line
        line = new Line(getStartX(), getStartY(), getEndX(), getEndY());
        line.setStroke(lineColor);
        this.lines.add(line);
        getChildren().add(line);
    }

    public void goLeft() {
        setEndX(getStartX() - this.lineLength);
        setEndY(getStartY());
    }

    public void goRight() {
        setEndX(getStartX() + this.lineLength);
        setEndY(getStartY());
    }

    public void goUp() {
        setEndX(getStartX());
        setEndY(getStartY() - this.lineLength);
    }

    public void goDown() {
        setEndX(getStartX());
        setEndY(getStartY() + this.lineLength);
    }
}

Using Default Constructor Using Default Constructor

Using Custom Coordinates Using Custom Coordinates

Answer

James_D picture James_D · Jul 25, 2014

You are invoking getWidth() and getHeight() in the constructor, which is necessarily executed before the Pane is added to any live scene. Hence these are going to return 0 (because the pane hasn't been laid out yet).

Instead, bind the coordinates of the line to values based on the widthProperty and heightProperty:

line = new Line();
line.startXProperty().bind(widthProperty().divide(2));
line.startYProperty().bind(heightProperty().divide(2));
line.endXProperty().bind(widthProperty().divide(2));
line.endYProperty().bind(heightProperty().divide(2).subtract(lineLength));