Reputation: 8357
I want a Rectangle
and a Label
to align in the StackPane, however my code doesn't achieve the desired result:
FXML:
<fx:root type="HBox" xmlns:fx="http://javafx.com/fxml">
<StackPane fx:id="pane">
<children>
<Rectangle fx:id="bubble" fill="#ffffff"></Rectangle>
<Label fx:id="message" style="-fx-border-color:black; -fx-border-width: 1; -fx-border-style: solid;"></Label>
</children>
</StackPane>
</fx:root>
Controller:
public class MessageBubble extends HBox implements Initializable {
@FXML
private StackPane pane;
@FXML
private Label message;
@FXML
private Rectangle bubble;
@Override
public void initialize(URL location, ResourceBundle resources) {
message.setText("message");
pane.setAlignment(Pos.CENTER);
bubble.setArcWidth(15.0);
bubble.setArcHeight(15.0);
bubble.setStroke(Color.BLACK);
message.widthProperty().add(10.0).addListener((observable, oldValue, newValue) -> {
bubble.setWidth(newValue.doubleValue());
pane.layout();
});
message.heightProperty().add(10.0).addListener((observable, oldValue, newValue) -> {
bubble.setHeight(newValue.doubleValue());
pane.layout();
});
}
public MessageBubble() {
FXMLLoader loader = new FXMLLoader(this.getClass().getResource("MessageBubble.fxml"));
loader.setRoot(this);
loader.setController(this);
try {
loader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Update
If I do sizing programmatically, it aligns fine:
this.pane.getChildren().addAll(this.bubble, this.message);
bubble.setFill(Color.ALICEBLUE);
bubble.setArcWidth(15.0);
bubble.setArcHeight(15.0);
bubble.setStroke(Color.BLACK);
message.setStyle("-fx-border-color:black; -fx-border-width: 1; -fx-border-style: solid;");
message.setText("message");
message.setPrefWidth(60.0);
message.setPrefHeight(25.0);
bubble.setWidth(70.0);
bubble.setHeight(30.0);
But this way, I need to calculate the size myself.
Upvotes: 0
Views: 3438
Reputation: 21809
Basically, if you need a Text
or Label
with colored backround, the easiest approach to use CSS for a single Label
or Text
, without any Rectangle
objects.
Example to use Label with CSS
Main.java
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
HBox root = new HBox();
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
Label message = new Label();
TextArea textArea = new TextArea();
textArea.setPrefWidth(200);
textArea.setText("message");
message.textProperty().bind(textArea.textProperty());
message.getStyleClass().add("rounded-background-label");
root.getChildren().addAll(message, textArea);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
application.css
.rounded-background-label {
-fx-background-color: black, white;
-fx-background-insets: 0, 1;
-fx-padding: 10px;
-fx-background-radius: 12px;
}
The answer from @jewelsea on this question is really detailed:
JavaFX 2: resizable rectangle containing text
But if you want to stay with the Label+Rectangle solution
I fear it is related to this bug: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8137252
As workaround you can request the layout update of the StackPane
, as you have tried in the listeners.
The only problem with your code is that you should replace:
pane.layout();
with
Platform.runLater(new Runnable() {
@Override
public void run() {
pane.requestLayout();
}
});
which will ensure that the layout will happen on the GUI Thread.
Example
LabelRectangleTest.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.shape.*?>
<HBox prefHeight="100.0" prefWidth="200.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="application.LabelRectangleTestController">
<children>
<HBox prefHeight="100.0" prefWidth="200.0">
<children>
<StackPane fx:id="pane" minHeight="126.0" prefHeight="126.0" prefWidth="200.0" HBox.hgrow="ALWAYS">
<children>
<Rectangle fx:id="bubble" arcHeight="15.0" arcWidth="15.0" fill="WHITE" height="27.75" stroke="BLACK" strokeType="INSIDE" width="68.0" />
<Label fx:id="message" text="Label" />
</children>
</StackPane>
<TextArea fx:id="textArea" prefWidth="200.0" wrapText="true" />
</children>
</HBox>
</children>
</HBox>
LabelRectangleTestController.java
public class LabelRectangleTestController implements Initializable {
@FXML
private Label message;
@FXML
private Rectangle bubble;
@FXML
private StackPane pane;
@FXML
private TextArea textArea;
@Override
public void initialize(URL location, ResourceBundle resources) {
textArea.setText("message");
message.textProperty().bind(textArea.textProperty());
message.widthProperty().addListener((observable, oldValue, newValue) -> {
bubble.setWidth(newValue.intValue() + 10);
Platform.runLater(new Runnable() {
@Override
public void run() {
pane.requestLayout();
}
});
});
message.heightProperty().addListener((observable, oldValue, newValue) -> {
bubble.setHeight(newValue.doubleValue() + 10);
Platform.runLater(new Runnable() {
@Override
public void run() {
pane.requestLayout();
}
});
});
}
}
Main.java
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("LabelRectangleTest.fxml"));
HBox root = loader.load();
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Upvotes: 2