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.
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>
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With