JavaFX Text inside Pane Layout is overlapping other Elements when using translateX() and translateY()

好久不见. 提交于 2019-12-22 09:11:08

问题


The image below is the description of what is happening:


I have a Pane which contains a Text and i am using the code below to make a marquee like effect of the Text . So when the Pane has not enough space to display the text , an animation is starting and the text has to go back and forth so the user can see it whole.

The problem is that i was expecting the text when going to the left using setTranslateX() to disappear and not overlape the other elements.For example here is overlapping the StackPane at the left.

The code comes from -> JavaFX Marquee Animation question.

  • Why it happens ?
  • I need a solution and a brief description for that :)

Below is the code:

Marquee.java [ class ]:

import java.io.IOException;

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.InvalidationListener;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.Pane;
import javafx.scene.text.Text;
import javafx.util.Duration;

/**
 * When the screen element is not big enough to show the text then an animation
 * will start automatically
 * 
 *
 */
public class Marquee extends Pane{

    @FXML
    private Text text;

    // minimum distance to Pane bounds
    private static final double OFFSET = 5;

    private Timeline timeline = new Timeline();

    /**
     * Constructor
     */
    public Marquee() {

        // FXMLLOADER
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Marquee.fxml"));
            loader.setController(this);
            loader.setRoot(this);
            loader.load();
        } catch (IOException ex) {
            ex.printStackTrace();
        }

    }


    /**
     * Called when FXML is loaded
     */
    @FXML
    public void initialize() {

        startAnimation();
    }

    /**
     * This method changes the text of the Marquee
     * 
     * @param value
     * @return this
     */
    public Marquee setText(String value) {

        // text
        text.setText(value);

        return this;
    }

    /**
     * This method starts the Animation of the marquee
     */
    private final void startAnimation() {

        // KeyFrame
        KeyFrame updateFrame = new KeyFrame(Duration.millis(35), new EventHandler<ActionEvent>() {

            private boolean rightMovement;

              @Override
                public void handle(ActionEvent event) {
                    double textWidth = text.getLayoutBounds().getWidth();
                    double paneWidth = getWidth();
                    double layoutX = text.getLayoutX();

                    if (2 * OFFSET + textWidth <= paneWidth && layoutX >= OFFSET) {
                        // stop, if the pane is large enough and the position is correct
                        text.setLayoutX(OFFSET);
                        timeline.stop();
                    } else {
                        if ((rightMovement && layoutX >= OFFSET) || (!rightMovement && layoutX + textWidth + OFFSET <= paneWidth)) {
                            // invert movement, if bounds are reached
                            rightMovement = !rightMovement;
                        }

                        // update position
                        if (rightMovement) {
                            layoutX += 1;
                        } else {
                            layoutX -= 1;
                        }
                        text.setLayoutX(layoutX);
                    }
                }
        });
        timeline.getKeyFrames().add(updateFrame);
        timeline.setCycleCount(Animation.INDEFINITE);

        // listen to bound changes of the elements to start/stop the
        // animation
        InvalidationListener listener = o -> {
            double textWidth = text.getLayoutBounds().getWidth();
            double paneWidth = getWidth();
            if (textWidth + 2 * OFFSET > paneWidth && timeline.getStatus() != Animation.Status.RUNNING)
                timeline.play();
        };

        text.layoutBoundsProperty().addListener(listener);
        widthProperty().addListener(listener);

    }

}

Marquee.fxml :

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Text?>

<fx:root maxHeight="25.0" minHeight="25.0" prefHeight="25.0" prefWidth="100.0" style="-fx-border-color: orange;" type="Pane" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <Text fx:id="text" layoutX="5.0" layoutY="17.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Text" />
   </children>
</fx:root>

And finally Main.java [ class ] :

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {

        Scene scene = new Scene(new Marquee().setText("Extra Biiggggggg Textttt"), 300, 300);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

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


Edit after Fabians answer:

The .fxml now is:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Text?>
<?import javafx.scene.shape.Rectangle?>

<fx:root fx:id="root" maxHeight="25.0" minHeight="25.0" prefHeight="25.0" prefWidth="100.0" style="-fx-border-color: orange; -fx-background-color: white;" type="Pane" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1">
  <children>
     <Text fx:id="text" layoutX="5.0" layoutY="17.0" strokeType="OUTSIDE" strokeWidth="0.0" style="-fx-text-fill: black; -fx-font-weight: bold;" text="Stopped" />
  </children>
  <clip>
     <Rectangle width="${root.width}" height="${root.height}"/>
  </clip>
</fx:root>

回答1:


Why does it do that?

JavaFX by default also draws children outside of a nodes bounds.

How to fix that?

Use the clip property of the parent to clip the content at the bounds of the Pane:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Text?>
<?import javafx.scene.shape.Rectangle?>

<fx:root xmlns:fx="http://javafx.com/fxml/1" fx:id="root" maxHeight="25.0" minHeight="25.0" prefHeight="25.0" prefWidth="100.0" style="-fx-border-color: orange;" type="Pane" xmlns="http://javafx.com/javafx/8.0.60">
    <children>
        <Text fx:id="text" layoutX="5.0" layoutY="17.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Text"/>
    </children>
    <clip>
        <Rectangle width="${root.width}" height="${root.height}" />
    </clip>
</fx:root>


来源:https://stackoverflow.com/questions/40990292/javafx-text-inside-pane-layout-is-overlapping-other-elements-when-using-translat

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!