Updating JavaFX Label doesn't work with DataBinding - java

I tried to write my 'own' log in a tab and I experienced problems with updating the label with a naive solution that I had.
So after I googled I checked out this solution here:
Displaying changing values in JavaFx Label
I don't know if I did everything right but unfortunately this solution doesn't work for me.
final static Label logLabel = new Label();
final static SimpleStringProperty logString = new SimpleStringProperty("test");
...
...
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
tabPane.getTabs().add(createSettingsTab());
tabPane.getTabs().add(createParticipantTab());
tabPane.getTabs().add(createSpectatorTab());
tabPane.getTabs().add(createOverviewTab());
tabPane.getTabs().add(createTournamentTab());
tabPane.getTabs().add(createLogTab());
tabPane.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
Scene scene = new Scene(tabPane, 1200, 800);
primaryStage.setScene(scene);
primaryStage.setResizable(true);
primaryStage.show();
}
private Tab createLogTab() {
// TODO Auto-generated method stub
logLabel.textProperty().bind(logString);
Tab tab = new Tab("Log");
tab.setContent(logLabel);
return tab;
}
I got this lines for initializing the Label and for setting the new text I do this:
logString.set(logString.get() + "foo");
The log tab keeps being blank...
I'd appreciate any help! Thanks!
edit// That's the only useful mcve I can think of. As I said the other create methods to create the other tabs are not making use of the label or the SimpleStringProperty
This is the button that doesn't work as supposed concerning the label.
buttonLoadConfig.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
System.out.println("test");
logString.set(logString.get() + "\ttest");
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Choose Config");
File config = fileChooser.showOpenDialog(new Stage());
}
});

Use of static members is often not a good idea and is probably contributing to your issue.
Your code as it is currently written can have many tabs but only one label. You are trying to add the same label to multiple tabs. But, when you do that, the label will be removed from the previous tabs because of rules on how the JavaFX Scenegraph works.
See the Node documentation:
If a program adds a child node to a Parent (including Group, Region, etc) and that node is already a child of a different Parent or the root of a Scene, the node is automatically (and silently) removed from its former parent.
So the binding is working, but, due to the above rule and the way you have written the rest of your code, the program as a whole is likely not working as you expected or wished.
If you are still having issues, update your question with an mcve.

Related

JavaFX setting root of scene causes root to change size

I have found that when I set the root of a scene (as advised - instead of simply setting the scene), the root pane is resized and does not fit in the stage properly, even though I supply the same dimensions as the stage. Screenshots can be seen in the links below.
If I use the setScene function, it works as expected and the root pane is the correct size, but there is a flash of a white screen between scene changes.
Here are examples from my project:
// resizes root pane - too small for stage
public void handle(MouseEvent event) {
if (event.getSource() instanceof Button) {
Button sourceButton = (Button)event.getSource();
Stage primaryStage = (Stage ((Node)event.getSource()).getScene().getWindow();
if (sourceButton.getId().equals("back-button")) {
primaryStage.getScene().setRoot(view.getWelcomePane());
}
}
}
public Pane getWelcomePane() {
return new BorderPane(bpWelcomeMain); // bpWelcomeMain is the border pane that contains the page components
}
Or:
// produces 'flashing' between scene transition (but root pane is correct size)
public void handle(MouseEvent event) {
if (event.getSource() instanceof Button) {
Button sourceButton = (Button)event.getSource();
Stage primaryStage = (Stage ((Node)event.getSource()).getScene().getWindow();
if (sourceButton.getId().equals("back-button")) {
primaryStage.setScene(view.getWelcomeScene());
}
}
}
public Pane getWelcomeSceme() {
return new Scene(bpWelcomeMain, 1500, 750); // bpWelcomeMain is the border pane that contains the page components
}
I have tried sizeToScene() as well as other things mentioned on other posts, but I am yet to discover a solution. The function setRoot() seems to work on one of my pages but not the others, even though they are all the same dimensions and the code for switching between the root panes is the same. I don't understand why setRoot() works for one but doesn't work for the other pages. I also don't understand why setScene() produces the correctly sized root pane but setRoot() does not.
So, I would like to use the setRoot() function without the root pane resizing, or setScene() without the flash between scene transitions (it ruins the fluidity of the UI). Is there a way?
This is the result of setScene() (although it looks good because of the flashing between scene changes I feel like an epilepsy warning may be needed haha)
This is the result of setRoot()
Any help would be greatly appreciated.

JavaFX - Adding a ScrollPane popup which closes when user clicks out of it

My title is badly worded because my problem is very hard to describe, so I drew an image for it:
I have an ImageView object which represents a pile of cards (not poker cards but just used them as an example). When this image view is clicked, I need a window to popup that features a ScrollPane and shows them all the card objects that are in the linked list. When the user clicks anywhere outside of the window (and later on, any right mouse button click) the scrollpane window needs to close.
Ways that I have already tried:
Scene with APPLICATION_MODAL. Then did Scene.showAndWait(). I didn't like this method because it made another application on the user's taskbar. It also felt clunky and just bad.
Changed my root pane to a StackPane, then added this Scrollpane to the stackpane when the user clicked on the deck. This for some reason was really slow and seemed really obtrusive. It was also annoying because my alternate class needed to have access to the root pane (since when it closes, it needs to go to the root StackPane and call .remove() on itself).
Are there any other better ways to accomplish this? My application is going to have many of these piles and so this framework needs to work very well.
I would still propose to open a new Stage with some restrictions to solve your issues with this approach.
You can use the initOwner property of the Stage to have another Stage as owner, therefore no other icon will appear on the taskbar.
You can use the initStyle property with TRANSPARENT or UNDECORATED StageStlye, this will ensure that only the content is visible.
And in the end you can use the focusedProperty to check whether the Stage lost focus to close it.
Example
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
Button b = new Button("Open deck");
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Stage popupStage = new Stage();
popupStage.initOwner(primaryStage);
popupStage.initStyle(StageStyle.UNDECORATED);
Scene sc = new Scene(new ScrollPane(), 300, 300);
popupStage.setScene(sc);
popupStage.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue,
Boolean newValue) {
if(!newValue)
popupStage.close();
}
});
popupStage.show();
}
});
root.setCenter(b);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
It is also possible of course to not open it in a new Stage, but draw a ScrollPane inside the current Stage which overlaps the content of the Stage using for example an AnchorPane or Group as root, but the first solution has the advantage that you are not bound to the dimensions of main Stage (the popup can have any size that you want).
You can achieve this with a low level system hook that catches the mouse events.
http://kra.lc/blog/2016/02/java-global-system-hook/ or https://github.com/kwhat/jnativehook/releases
I hope that is what you needed, otherwise i got your question wrong.

Delete selected row from TableView, and the effective use of SceneBuilder in JavaFX

I'm trying to make an application that creates a table from text in a file and then the user can add or delete words. The text file is used else where in my app to populate dropdown boxes so the user can only choose the text in the dropdown boxes.
My problem is I can't delete from the table only add to it and anything I can find on the net are tables that are made from classes. My table is just a very basic one that has 1 column of strings. For some reason when I print the selected items of the table I always get nothing? I can't figure out why.
I only started javaFx a few weeks ago I really just learning from videos and books but they are very simple compared to the application I'm expected to make.
Here is my code:
public class MaintenWindow {
private static Stage window;
private static TableView<String> table = new TableView<String>();
private static TextField input;
private static ObservableList<String> stringList;
private static BorderPane bP;
private static Scene scene;
private static File types = new File("type.txt");
private static File location = new File("location.txt");
private static File status = new File("status.txt");
public static void display() throws FileNotFoundException {
window = new Stage();
BorderPane bP = new BorderPane();
window.setTitle("Mainten Window");
MenuBar menuBar = new MenuBar();
Menu fileMenu = new Menu("_Add/Remove From DropDowns");
MenuItem editLocation = new MenuItem("_Edit Locations");
MenuItem editAtypes = new MenuItem("_Edit Animal Types");
MenuItem editStatus = new MenuItem("_Edit Status's");
MenuItem exit = new MenuItem("Exit");
fileMenu.getItems().addAll(editLocation, editAtypes, editStatus, new SeparatorMenuItem(),exit);
menuBar.getMenus().addAll(fileMenu);
editLocation.setOnAction(e -> {
bP.setTop(menuBar);
bP.setCenter(vBox("Locations", location));
window.setScene(scene);
window.show();
});
editAtypes.setOnAction(e -> {
bP.setTop(menuBar);
bP.setCenter(vBox("Animal Types", types));
window.setScene(scene);
window.show();
});
editStatus.setOnAction(e -> {
bP.setTop(menuBar);
bP.setCenter(vBox("Status", status));
window.setScene(scene);
window.show();
});
bP.setTop(menuBar);
scene = new Scene(bP, 800,800);
Label label = new Label("welcome to the maintenance window");
bP.setCenter(label);
window.setScene(scene);
window.show();
}
/*public static MenuBar menuBar()
{
//file tab menu
Menu fileMenu = new Menu("_Add/Remove From DropDowns");
MenuItem editLocation = new MenuItem("_Edit Locations");
MenuItem editAtypes = new MenuItem("_Edit Animal Types");
MenuItem editStatus = new MenuItem("_Edit Status's");
MenuItem exit = new MenuItem("Exit");
fileMenu.getItems().addAll(editLocation, editAtypes, editStatus, new SeparatorMenuItem(),exit);
editLocation.setOnAction(e -> {
bP.setTop(menuBar());
bP.setCenter(vBox("Locations", location));
window.setScene(scene);
window.show();
});
editAtypes.setOnAction(e -> {
bP.setTop(menuBar());
bP.setCenter(vBox("Animal Types", types));
window.setScene(scene);
window.show();
});
editStatus.setOnAction(e -> {
bP.setTop(menuBar());
bP.setCenter(vBox("Status", status));
window.setScene(scene);
window.show();
});
MenuBar menuBar = new MenuBar();
menuBar.getMenus().addAll(fileMenu);
return menuBar;
}*/
public static ObservableList<String> getString(File file) throws FileNotFoundException{
stringList = FXCollections.observableArrayList();
Scanner kb = new Scanner(file);
String item;
while (kb.hasNext()) {
item = kb.nextLine();
stringList.add(item);
}
kb.close();
return stringList;
}
#SuppressWarnings("unchecked")
public static TableView<String> table(String string,File file)
{
TableColumn<String, String> tColumn = new TableColumn<String, String>(string);
tColumn.setCellValueFactory(cellData ->
new ReadOnlyStringWrapper(cellData.getValue()));
TableView<String> table = new TableView<String>();
try {
table.setItems(getString(file));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
table.getColumns().addAll(tColumn);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
return table;
}
public static void addButtonClicked(){
stringList.add(input.getText());
input.clear();
}
//Delete button clicked
public static void deleteButtonClicked(){
ObservableList<String> allStrings, stringSelected;
allStrings = table.getItems();
//printElements(allStrings);
System.out.println(table.getSelectionModel().getSelectedItems());
stringSelected = table.getSelectionModel().getSelectedItems();
System.out.println(stringSelected);
stringSelected.forEach(allStrings::remove);
}
public static HBox textFields() {
input = new TextField();
input.setPromptText("Input");
input.setMinWidth(75);
HBox hBox = new HBox();
hBox.setPadding(new Insets(10,10,10,10));
hBox.setSpacing(10);
hBox.getChildren().addAll(input);
return hBox;
}
public static HBox addRemove(File file){
//Button
Button addButton = new Button("Add");
addButton.setOnAction(e -> addButtonClicked());
Button deleteButton = new Button("Delete");
deleteButton.setOnAction(e -> deleteButtonClicked());
Button writeButton = new Button("Write to file");
try {
try {
writeButton.setOnAction(e -> write(file));
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Button closeButton = new Button("Close Window");
closeButton.setOnAction(e -> window.close());
HBox hBox2 = new HBox();
hBox2.setPadding(new Insets(10,10,10,10));
hBox2.setSpacing(10);
hBox2.getChildren().addAll(addButton, deleteButton, writeButton, closeButton);
return hBox2;
}
private static void printElements(ObservableList<String> list) {
System.out.println("Size: " + list.size());
for (Object o : list) {
System.out.println(o.toString());
}
System.out.println("");
}
public static void write(File file)
{
PrintWriter outFile = null;
try {
outFile = new PrintWriter(file);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for(int i = 0; i < stringList.size(); i++)
{
outFile.println(stringList.get(i));
}
outFile.close();
}
public static VBox vBox(String string, File file){
Label label = new Label(string);
label.setFont(new Font("Arial", 20));
VBox vBox = new VBox();
vBox.setSpacing(5);
vBox.setPadding(new Insets(10, 0, 0, 10));
vBox.getChildren().addAll(label, table(string,file), textFields(), addRemove(file));
return vBox;
}
}
I would be greatful for any help.
if u have any tips or advice for me please post
If you have links to more advanced javafx programs than I'm finding please post them.
What's going wrong
You have a logic error in your code where you create two instances of your table. You declare in your class:
private static TableView<String> table = new TableView<String>();
Then later on in your table() function you declare a new table local to that function
TableView<String> table = new TableView<String>();
table.setItems(getString());
table.getColumns().addAll(tColumn);
return table;
Then you eventually add the table returned by your table() function to the scene:
vbox.getChildren().addAll(label, table(), textFields(), addRemove());
But elsewhere in your application, such as deleteButtonClicked(), you invoke:
table.getSelectionModel().getSelectedItems();
But that is going to be working off the instance declared in your class, not the instance declared in your method, so it will always return an empty list.
How to fix it
To fix this, only create a single new TableView, not two.
Here is some simplified sample code:
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class MaintenanceWindow extends Application {
private TableView<String> table;
private TextField input;
private Stage myStage;
private static final String[] ITEMS = { "apples", "oranges", "peaches", "pears" };
#Override
public void start(Stage stage) throws Exception {
myStage = stage;
final Label label = new Label("list");
label.setFont(new Font("Arial", 20));
VBox vbox = new VBox(
5,
label, table(), textFields(), addRemove()
);
vbox.setPadding(new Insets(10, 0, 0, 10));
BorderPane bP = new BorderPane();
bP.setTop(menuBar());
bP.setCenter(vbox);
stage.setScene(new Scene(bP, 700, 800));
stage.show();
}
private MenuBar menuBar() {
Menu fileMenu = new Menu("_Add/Remove From DropDowns");
MenuItem locationAdd = new MenuItem("Location DropDowns Add");
locationAdd.setOnAction(e -> addButtonClicked());
MenuItem locationRemove = new MenuItem("Location DropDowns Remove");
locationRemove.setOnAction(e -> deleteButtonClicked());
MenuItem exit = new MenuItem("Exit");
exit.setOnAction(e -> closeApp());
fileMenu.getItems().addAll(
locationAdd,
new SeparatorMenuItem(),
locationRemove,
new SeparatorMenuItem(),
exit
);
MenuBar menuBar = new MenuBar();
menuBar.getMenus().addAll(fileMenu);
return menuBar;
}
private TableView<String> table() {
TableColumn<String, String> tColumn = new TableColumn<>("String");
tColumn.setMinWidth(250);
tColumn.setCellValueFactory(
cellData -> new ReadOnlyStringWrapper(cellData.getValue())
);
table = new TableView<>();
table.getItems().addAll(ITEMS);
table.getColumns().add(tColumn);
return table;
}
private void addButtonClicked() {
if (!(input.getText() == null || "".equals(input.getText().trim())) {
table.getItems().add(input.getText());
input.clear();
}
}
private void deleteButtonClicked() {
table.getItems().removeAll(
table.getSelectionModel().getSelectedItems()
);
}
private HBox textFields() {
input = new TextField();
input.setPromptText("Input");
input.setMinWidth(75);
HBox hBox = new HBox();
hBox.setPadding(new Insets(10, 10, 10, 10));
hBox.setSpacing(10);
hBox.getChildren().addAll(input);
return hBox;
}
private HBox addRemove() {
Button addButton = new Button("Add");
addButton.setOnAction(e -> addButtonClicked());
Button deleteButton = new Button("Delete");
deleteButton.setOnAction(e -> deleteButtonClicked());
Button closeButton = new Button("Close Window");
closeButton.setOnAction(e -> closeApp());
HBox hBox2 = new HBox(
10,
addButton, deleteButton, closeButton
);
hBox2.setPadding(new Insets(10, 10, 10, 10));
hBox2.setSpacing(10);
return hBox2;
}
private void closeApp() {
myStage.close();
}
}
Unrelated advice for future questions:
Those not looking for advice can skip reading the rest of this answer as it doesn't really apply directly to the question.
Create an mcve.
Follow the steps for debugging small programs. If you have a large program and can't work out what is going on, just create a small program.
As your program gets larger, either design it with multiple classes from the start or refactor it to use multiple classes.
Use FXML and SceneBuilder for complex layouts such as this rather than hand coding them.
Go through the makery JavaFX tutorial and study it very closely (it seems pretty similar to what you are trying to achieve here).
Just ask a single question per question on StackOverflow and isolate the code that addresses each single question to a specific mcve that only addresses that. e.g. for your original additional question on file exceptions (which it looks like somebody has now edited out of your question), just post that as a seperate question.
When you have a program that works (but is not too large), post its code to codereview for feedback on general programming techniques, conventions and design approaches (for example they will certainly give you feedback not use static methods everywhere). For an example of the kinds of things you should post to code review and the kind of feedback you can expect, see the coderview for An alarm application in Java(FX).
Stackoverflow isn't really a tips and advice site, it just pure Q&A.
Would u recommend scenebuilder for novice javafx programmers?
Yes. I used to not not recommend it for beginners, but have come around to thinking it is a good idea to use that tool for initial experimentation and later development, even for novice JavaFX programmers. Using the SceneBuilder tool rather than directly programming against the Java API, it is probably easier and more efficient for a new developer to get insight into how layouts work and what properties are exposed on nodes, regions and various controls.
However, learning SceneBuilder and FXML alone is no substitute for actually learning how to program against the Java API, for which you need to write code. So definitely do both things, play around with stuff in SceneBuilder and write code against the Java API. At first do those things separate, by exploring JavaFX controls and layouts only in scene builder and reviewing the FXML it generates and by writing small standalone programs that are coded just against the Java API, with no use of FXML. Then, only once you are relatively comfortable with both of those things, tackle a project like that in the Makery tutorial linked earlier, that combines Scene Builder for definition of FXML based UIs and usage of those FXML files in an intermediate sized application that also includes code against the Java API.
Also, neither playing with SceneBuilder nor playing around with the API, is a substitute for working through the JavaFX tutorials and understanding layout fundamentals such as Groups, layout panes and Regions, which must be done to get a reasonable grasp on the technology.
No JavaFX program can rely on FXML alone, if you use FXML, you need a combination of FXML and Java code to get a working program. Any significant JavaFX program that uses a lot of controls and layouts is best off being coded with FXML layout, CSS for the style and Java (or other language) code for the logic. So, eventually you need to learn all three technologies (FXML, CSS and JavaFX API) and how to make them function together in order to proceed beyond trivial programs.
One issue with SceneBuilder which I see new JavaFX developers encounter, is that is is relatively simple to create a complex UI in SceneBuilder, but it is relatively hard for a novice to take that complex UI and add correct logic to it to make the application actually functional as it should be. So, don't make the mistake of defining the complete UI for your application in SceneBuilder before you gain a decent understanding of both how to use the JavaFX Java API and how to add logic to some very basic FXML files using the JavaFX Java API.
Also once you start coding up multiple FXML files, a problems which everybody runs into is:
Loading new fxml in the same scene
Passing Parameters JavaFX FXML
i choose not to use it as i thought it looked very complicated since u would need an xml and controller
Yeah, it is (relatively speaking), more complicated to understand and you have to have more skills to initially create an FXML/CSS based application than something written against a pure Java API. However, once you have built one, my guess is that you will find an intermediate sized FXML/CSS based application easier to understand and maintain than an equivalent application written purely against the Java API.

JavaFX GUI doesn't release memory

I've been struggling for weeks to resolve the memory leaks within our JavaFX application, and thought today I was at a complete loss with it so decided to write the simplest application application I could think of to prove JavaFX could in fact release memory and therefore proving I was doing something wrong. To my surprise this seems to leak memory too.
Can anyone advise why the following application still has a javafx.scene.control.Label in the heap after the button is clicked? The way I checked it was there was with jprofiler.
public class MemoryTestApplication extends Application {
#Override
public void start(Stage primaryStage) {
//root pane
final VBox root = new VBox();
//label to remove when button is clicked
final Label label = new Label("Remove Me");
root.getChildren().add(label);
//button to remove label
Button btn = new Button("Remove label");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
root.getChildren().remove(label);
}
});
root.getChildren().add(btn);
//create scene and stage
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Your anonymous inner class for the event handler is holding a reference to the label, and your button is holding a reference to the event handler.
The anonymous inner class is going to have two synthetically generated fields, created by the compiler, like:
final Label val$label;
final MemoryTestApplication this$0;
Since these are final, you can't clear label after using it by label = null;, so I think your best bet is to change the event handler to a normal, named class, pass the label reference in to its constructor, and then clear it after using it to remove the label from the layout. (You would want to test that it was not null before removing, so that it would only be removed the first time the button is pressed).

Explicitly positioning nodes in JavaFX

When I have clicked a button, it changes its position.
But when I move the mouse, the button comes back to the center of the scene, why?
I have the following code:
public class HolaMundo extends Application {
Button btn;
Scene scene;
#Override
public void start(Stage primaryStage) {
btn = new Button();
btn.setText("Hola Mundo");
StackPane root = new StackPane();
root.getChildren().add(btn);
scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
scene.setOnMouseMoved(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent t) {
btn.setText(String.valueOf(t.getX() ));
}
});
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
btn.setLayoutX(Math.random() * (300 - btn.getWidth()));
btn.setLayoutY(Math.random() * (250 - btn.getHeight()));
}
});
}
public static void main(String[] args) {
launch(args);
}
}
Suggested approach
For your particular code snippet, perhaps using a Pane instead of a StackPane is the best approach.
Why your code doesn't do what you want
If you are going to manually set layout values, don't use a layout pane (such as a StackPane) which automatically sets layout values for you. If you use a layout pane, then the layout values you explicitly set will be overridden automatically the next time layout pane performs a layout pass (e.g. it is resized or some its content or location in the scene graph becomes dirty).
Options for explicitly laying out nodes
If you want to explicitly layout items in JavaFX do one of the following:
Subclass Region and override layoutChildren.
Place your content in a Pane if you need the container to be styled with CSS or implement resizing functions.
Place your content in a Group if you don't need the container to be styled with CSS or implement resizing functions.
Related advice on node positioning
If you want to have automatically laid out components using the predefined layout managers but you want to adjust or temporarily modify the locations of some of the components from their default positions, you can adjust the translateX/Y values rather than layoutX/Y values. According to the definition of translateX:
The node's final translation will be computed as layoutX + translateX, where layoutX establishes the node's stable position and translateX optionally makes dynamic adjustments to that position.
This variable can be used to alter the location of a node without disturbing its layoutBounds, which makes it useful for animating a node's location.
This means that the layout manager can compute a child's default position using layoutX, and you can adjust the position from the default using translateX.
Not having deeply investigated the current case, I see a difference when I use an AnchorPane instead of the StackPane to place the Button on.
By changing the label text of the Button by the mouseMoved-Event the Pane is rendered (layout is requested). With a StackPane placing all it's children in the center of itself the Button's position is reset to the center of the Pane. When you have a look into the layoutChildren method of StackPane you'll see a call to resizeRelocate. So layoutX and layoutY are reset and the button moves back to the center position (or whatever you set the alignment of the StackPane to).
So I think this is a correct behavior of the StackPane and I recommend to use another Pane, e.g. AnchorPane.

Categories

Resources