GridPane acts as mouseTransparent although not set - javafx

What do I want to do?
I am facing a problem with a fxml based javafx application. I want to create a simple form sheet to enter some numerical values that are supposed to be stored in a file uppon confirmation.
What is the problem?
The form does perfectly what it is supposed to do, except that I cannot mouse click the elements within the two GridPanes. I can enter the values via switching TextFields by pressing the tab button, but that is of course not the ideal way. I have already added TextFields and CheckBoxes within the AnchorPane directly, and they are clickable, but within the GridPanes this does not work. The FlowPane within the same AnchorPane also works perfectly fine.
To me it seems that the GridPanes act as if it was set to mouseTransparent="true", but this is not the case and I already tried explicitly setting it to false as well. I cannot find the reason for this behavior so perhaps somebody of you has an idea.
EDIT: After playing around with an addtional GridPane in the same tab I found out that it is not a general GridPane issue in this file. For a GridPane with only checkboxes and only one Anchor the element can be accessed. Is there some overlap preventing accessability via mouse?
Within an initializer function of a separate class the scene is set:
BorderPane root = FXMLLoader.load(getClass().getResource("..\\initializeSeason\\InitializeSeason.fxml"));
Scene scene = new Scene(root,600,900);
stage.setScene(scene);
stage.show();
The xfml file is build as follows (I'm sorry for the German names):
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.layout.FlowPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.text.Text?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.CheckBox?>
<BorderPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.LeagueRulesController" >
<center>
<TabPane tabClosingPolicy="UNAVAILABLE" >
<tabs>
<Tab style="-fx-font-weight:bold" text="Liga Regeln" >
<AnchorPane >
<children>
<GridPane AnchorPane.topAnchor="10.0" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" hgap="15" vgap="15" >
<columnConstraints>
<ColumnConstraints hgrow="ALWAYS" />
</columnConstraints>
<children>
<Label text="Anzahl der Zeichen im Teamnamen: " GridPane.columnIndex="0" GridPane.rowIndex="0"/>
<TextField fx:id="numberOfShortNameChars" alignment="BASELINE_RIGHT" prefWidth="100" GridPane.columnIndex="1" GridPane.rowIndex="0"/>
<Label text="Anzahl der Touchdown Differenz Einträge: " GridPane.columnIndex="0" GridPane.rowIndex="1"/>
<TextField fx:id="numberOfTDDiffEntries" alignment="BASELINE_RIGHT" prefWidth="100" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<GridPane fx:id="tdDiffEntrys" hgap="15" vgap="15" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<Label text="Erhält Gewinnerteam bei Spielaufgabe maximale Punktzahl?:" GridPane.columnIndex="0" GridPane.rowIndex="3"/>
<CheckBox fx:id="isConcedeMaxWin" GridPane.columnIndex="1" GridPane.rowIndex="3"></CheckBox>
</children>
</GridPane>
<FlowPane orientation="HORIZONTAL" alignment="BOTTOM_RIGHT" hgap="5" AnchorPane.topAnchor="10.0" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0">
<Button fx:id="okButton" text="OK" onAction="#handleOK"> </Button>
<Button fx:id="abbrechenButton" text="Abbrechen" onAction="#handleAbbrechen"> </Button>
</FlowPane>
</children>
</AnchorPane>
</Tab>
<Tab style="-fx-font-weight:bold" text="Teams verwalten">
</Tab>
</tabs>
</TabPane>
</center>
</BorderPane>
Although I am sure the problem can be found within the .fxml file I also add the LeagueRulesController class.
package application;
import java.net.URL;
import java.util.ArrayList;
import java.util.ResourceBundle;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class LeagueRulesController implements Initializable{
private ArrayList<TextField> tdDiffs;
private ArrayList<TextField> points;
#FXML
private CheckBox isConcedeMaxWin;
#FXML
private Button okButton;
#FXML
private Button abbrechenButton;
#FXML
private void handleAbbrechen(ActionEvent actionEvet) {
Stage stage = (Stage) abbrechenButton.getScene().getWindow();
// do what you have to do
stage.close();
}
#FXML
private void handleOK(ActionEvent actionEvet) {
Stage stage = (Stage) abbrechenButton.getScene().getWindow();
stage.close();
}
#FXML
private TextField numberOfShortNameChars;
#FXML
private TextField numberOfTDDiffEntries;
#FXML
private GridPane tdDiffEntrys;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
numberOfShortNameChars.setText("25");
numberOfShortNameChars.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
numberFieldListener(numberOfShortNameChars, oldValue,newValue);
}
});
numberOfTDDiffEntries.setText("5");
initializeTDDiffArray();
numberOfTDDiffEntries.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
numberFieldListener(numberOfTDDiffEntries, oldValue,newValue);
initializeTDDiffArray();
}
});
}
private void initializeTDDiffArray() {
tdDiffEntrys.getChildren().clear();
tdDiffs = new ArrayList<>();
points = new ArrayList<>();
tdDiffEntrys.add(new Label("TD Diff"), 0, 0);
tdDiffEntrys.add(new Label("Punkte"), 1, 0);
int numberOfTDDiffEntriesInt;
if(numberOfTDDiffEntries.getText().equals(""))
numberOfTDDiffEntriesInt = 1;
else
numberOfTDDiffEntriesInt =Integer.parseInt(numberOfTDDiffEntries.getText());
for (int i = 0; i < numberOfTDDiffEntriesInt; i++) {
tdDiffs.add(new TextField());
points.add(new TextField());
tdDiffs.get(tdDiffs.size()-1).setText(Integer.toString(i-numberOfTDDiffEntriesInt/2));
points.get(points.size()-1).setText(Integer.toString(i+1));
tdDiffs.get(tdDiffs.size()-1).setAlignment(Pos.BASELINE_RIGHT);
points.get(points.size()-1).setAlignment(Pos.BASELINE_RIGHT);
tdDiffs.get(tdDiffs.size()-1).setPrefWidth(1);
points.get(points.size()-1).setPrefWidth(1);
tdDiffs.get(tdDiffs.size()-1).textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
numberFieldListener(tdDiffs.get(tdDiffs.size()-1), oldValue,newValue);
}
});
points.get(points.size()-1).textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
numberFieldListener(points.get(points.size()-1), oldValue,newValue);
}
});
tdDiffEntrys.add(tdDiffs.get(tdDiffs.size()-1), 0, i+1);
tdDiffEntrys.add(points.get(points.size()-1), 1, i+1);
}
}
private void numberFieldListener(TextField textField, String oldValue,String newValue) {
if(!newValue.matches("\\d*")) {
textField.setText(newValue.replaceAll("[^\\d]", ""));
}
}
}
Thanks in advance, for taking the time.

Your FlowPane is on top of your GridPane which stops mouse events from reaching the GridPane. In your FXML file you have the AnchorPane constraints for both your GridPane and FlowPane set to 10 (for top, left, bottom, and right). This means both nodes are taking up the same space in the AnchorPane. And since you add your FlowPane second the GridPane gets covered. Note that adding the GridPane second will most likely allow you to click the TextFields but will stop you from being able to click the Buttons.
Instead of an AnchorPane why don't you use something like a VBox? A VBox is designed for laying out nodes vertically. You could also replace the FlowPane with an HBox since that's the behavior you seem to be after. You'd end up with something like:
<Tab>
<VBox>
<GridPane>
<!-- your GirdPane children -->
</GridPane>
<HBox>
<!-- your buttons -->
</HBox>
</VBox>
</Tab>
I omitted attributes for brevity. You'd obviously set them to your desired values.
If you go this route you'll need to move your Label - the one with its text set to "Erhält Gewinnerteam bei Spielaufgabe maximale Punktzahl?:" - into the new HBox as the first child. That way it stays in line with the buttons.
Of course, you don't have to replace your AnchorPane. If you want to keep it, you'll have to figure out how to have the FlowPane's top anchor always be equal to how far the bottom of the GridPane is from the top. Since you currently have the GridPane's top anchor at 10 the FlowPane's top anchor would have to be something like 10 + gridPaneHeight. I'm not sure you can do this inside the FXML file which means you'd have to do it in your controller's initialize method. If your app is going to be resizable you may also have to update the top anchor whenever a resize occurs.

Related

More efficient way to add Panes to an HBox?

I am working on a project that is going to involve adding panes (or hboxes if that works better) to a very wide HBox. I set it up in an fxml pretty easily but I'm already going to have to make a few of these and I would love to avoid all the copying pasting. So I figured I should try and use a for loop to populate the HBox but for whatever reason it is not working. I'm in the very beginning steps so my code is very simple and straight forward.
This is the basic sample I'm trying to recreate
<HBox>
<children>
<Pane fx:id="pane0" prefHeight="200.0" prefWidth="200.0">
<children>
<Button layoutX="71.0" layoutY="95.0" mnemonicParsing="false" text="Button" />
</children></Pane>
<Pane fx:id="pane1" prefHeight="200.0" prefWidth="200.0">
<children>
<Button layoutX="71.0" layoutY="95.0" mnemonicParsing="false" text="Button" />
</children></Pane>
<Pane fx:id="pane2" prefHeight="200.0" prefWidth="200.0">
<children>
<Button layoutX="71.0" layoutY="95.0" mnemonicParsing="false" text="Button" />
</children></Pane>
</children>
</HBox>
So to create it dynamically (and for clarity) I created a class for the HBox and the panes. For now each pane just has a button but they will undergo further customization once this code works. Same with the HBox.
Here's the HBox
public class HBoxTestClass {
#FXML
HBox hBox = new HBox();
public HBoxTestClass(){
}
#FXML
public void initialize(){
populateHBox();
}
private void populateHBox(){
for (int i = 0; i < 3; i++){
hBox.getChildren().add(new TestPane());
hBox.setSpacing(10);
}
}
}
its fxml
<HBox fx:id="hBox" xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="sample.HBoxTestClass"
prefHeight="400.0" prefWidth="600.0">
</HBox>
The pane class and its fxml
public class TestPane extends Pane{
#FXML Pane testPane = new Pane();
#FXML Button button = new Button();
public TestPane(){
}
#FXML
private void initialize(){
button.setText("Click Me!");
}
}
<Pane fx:id="testPane" xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="sample.TestPane"
prefHeight="200.0" prefWidth="200.0">
<children>
<Button fx:id="button" layoutX="71.0" layoutY="95.0" mnemonicParsing="false" text="Button" />
</children>
</Pane>
So my very simple code above just produces a blank, wide screen with no panels in it. I put a console print in the constructor of my TestPane class so I know it's getting called but still nothing appears. Any advice? Thanks
I am not sure what you are doing in your code incorrectly. Code like #FXML Pane testPane = new Pane(); should look like #FXML Pane testPane;. I am not sure how code like #FXML
public void initialize(){ is being called in your program. Try to follow this MCVE.
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication363 extends Application
{
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
Controller
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
/**
*
* #author blj0011
*/
public class FXMLDocumentController implements Initializable
{
#FXML
private HBox hBox;
#Override
public void initialize(URL url, ResourceBundle rb)
{
// TODO
for (int i = 0; i < 5; i++) {
StackPane stackPane = new StackPane(new Label("Label: " + i));
hBox.getChildren().add(stackPane);
}
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.HBox?>
<HBox fx:id="hBox" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication363.FXMLDocumentController" />

Button won't work javafx

I'm a total beginner in programming. I've been trying to create a simple program that gets two values from the user (two grades from 0 to 10) and returns the average between them. I've done it with Swing and JOptionPane but using javafx I got nothing. Here goes the code.
package fxcalcmedia;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class FXCalcMedia extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root =
FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
stage.setTitle("CALCULADORA DE MÉDIA");
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
package fxcalcmedia;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.text.Text;
public class FXMLDocumentController implements Initializable {
#FXML
private Label lblMedia;
private Button btnCalc;
private Text txtMedia;
private Text txtNota1;
private Text txtNota2;
#FXML
private void clicouBotao(ActionEvent event) {
double nota1 = Double.parseDouble(txtNota1.getText());
double nota2 = Double.parseDouble(txtNota2.getText());
double media = (nota1+nota2)/2;
txtMedia.setText(Double.toString(media));
}
#Override
public void initialize(URL url, ResourceBundle rb) {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="155.0" prefWidth="303.0"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="fxcalcmedia.FXMLDocumentController">
<children>
<Label fx:id="lblMedia" layoutX="30.0" layoutY="119.0"
text="Média" />
<Label layoutX="28.0" layoutY="33.0" text="Nota 1" />
<Label layoutX="28.0" layoutY="76.0" text="Nota 2" />
<Button fx:id="btnCalc" layoutX="212.0" layoutY="71.0"
mnemonicParsing="false" onAction="#clicouBotao" text="Calcular"
/>
<TextField fx:id="txtNota1" layoutX="90.0" layoutY="28.0"
prefHeight="25.0" prefWidth="78.0" />
<TextField fx:id="txtNota2" layoutX="90.0" layoutY="71.0"
prefHeight="25.0" prefWidth="78.0" />
<TextField fx:id="txtMedia" layoutX="90.0" layoutY="114.0"
onAction="#clicouBotao" prefHeight="25.0" prefWidth="78.0" />
</children>
</AnchorPane>
So... the window appears, I insert the grades but when I click on the button to get the average grade, nothing happens. I've tried a lot of things, I've searched a lot... I hope somebody can help. Thanks again.
You need to place #FXML annotation on each Node that have fx:id="" in fxml file. It is necessary for FXMLLoader to create correct instances and inject them to controller.
Another problem is components type in fxml file. In fxml you use TextField
<TextField fx:id="txtNota1" layoutX="90.0" layoutY="28.0"
prefHeight="25.0" prefWidth="78.0" />
<TextField fx:id="txtNota2" layoutX="90.0" layoutY="71.0"
prefHeight="25.0" prefWidth="78.0" />
<TextField fx:id="txtMedia" layoutX="90.0" layoutY="114.0"
onAction="#clicouBotao" prefHeight="25.0" prefWidth="78.0" />
but in cotntroller you use Text:
private Text txtMedia;
private Text txtNota1;
private Text txtNota2;
Solution:
#FXML
private Label lblMedia;
#FXML
private Button btnCalc;
#FXML
private TextField txtMedia;
#FXML
private TextField txtNota1;
#FXML
private TextField txtNota2;
As an extension to #MBec's answer. A very nice tool for creating GUIs for javaFX is JavaFX Scene Builder. It provides you with drag & drop functionality and also gives templates for each fxml file's Controller class.

Slider above the x axis in a chart?

I am trying to implement a Slider that has an XYChart below and is supposed to slider along the values of the x-axis. New values for the y-axis are to be dynamilcally added, so the chart may grow vertically. That's why I decided, it has to be inside a ScrollPane.
Now the question is:
How can I reliably position a slider above and with the same length of the x-axis?
I have tried to bind properties concerning the width and position of the Slider and the NumberAxis in fxml and java. See minimal (not) working (as desired) example below:
MainView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.chart.*?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.Slider?>
<VBox fx:id="timeBox" prefHeight="600" prefWidth="800" maxHeight="600" maxWidth="800"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Slider fx:id="timeSlider" blockIncrement="30.0" max="360.0" />
<HBox fx:id="scrollBox" fitwidth="${timeBox.width}" VBox.vgrow="ALWAYS">
<children>
<ScrollPane fx:id="timeScrollPane" fitToHeight="true"
fitToWidth="true" fitwidth="${scrollBox.width}" HBox.hgrow="SOMETIMES">
<content>
<LineChart fx:id="timeChart">
<xAxis>
<NumberAxis fx:id="timeAxis" minorTickCount="1"
minorTickLength="1.0" side="TOP" tickLabelGap="30.0"
tickLength="1.0" tickUnit="1.0" upperBound="360.0" />
</xAxis>
<yAxis>
<CategoryAxis fx:id="appearanceAxis" prefWidth="160.0"
side="LEFT" />
</yAxis>
</LineChart>
</content>
</ScrollPane>
</children>
</HBox>
</children>
</VBox>
MainViewController
public class MainViewController implements Initializable {
#FXML private VBox timeBox;
#FXML private HBox sliderBox;
#FXML private Slider timeSlider;
#FXML private HBox scrollBox;
#FXML private ScrollPane timeScrollPane;
#FXML private NumberAxis timeAxis;
#FXML private CategoryAxis appearanceAxis;
#FXML private LineChart<Number, String> timeChart;
#Override
public void initialize(URL location, ResourceBundle resources) {
// idea: set the same x value, but it does not change anything
timeSlider.setLayoutX(timeAxis.getLayoutX());
// idea: bind the width, but it stays longer
timeSlider.prefWidthProperty().bind(timeAxis.widthProperty());
}
}
Main.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("MainView.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root,800,600);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
I know, what I have tried so far is not really much, but I just don't have an idea how to fix that.
The chart is meant to show the start-to-stop time of a video file on the x-axis and annotations with their appearance intervals on the y-axis.
Thanks for reading this…

Why isnt my JavaFX Progress Indicator not updating?

I have the following layout in my JavaFX application,
StackPane
- BorderPane
- Text (on Top)
- Vbox (on Center)
- ProgressIndicator
- Label
- BorderPane
- Scroll Pane (with Tile Pane inside) (Center of Border Pane)
- Button (Bottom of Border)
- BorderPane
- Vbox (on Center)
- Progress Indicator
- Label
- Button ( Bottom)
When showing the first Border Pane, the Progress Indicator works perfectly. I set it up using the following code,
progress.progressProperty().bind(iconLoadTask.progressProperty());
progresspane.visibleProperty().bind(iconLoadTask.runningProperty());
When the iconLoadTask completes, the progress pane becomes invisible and the next pane is shown correctly.
From the next BorderPane (which has the Scroll pane), I show the last border pane (with a progress indicator - indeterminate). This progress indicator doesnt animate at all.
Edit:
As suggested, I have tried reproducing this issue,
I have the following controller file. Here you can see two progress indicators (progress and stopProfInd). progress is updated from the task appLoadTask. This is a finite task and I can update progress from the task. This works fine.
I have another indicator called stopProfInd. This is set indeterminate from Scene Builder. Here you can see that I make that pane visible. I expect the indicator to animate, but it doesn't.
public class AppController
implements Initializable {
#FXML
private StackPane stackpaneapps;
#FXML
private BorderPane progresspane;
#FXML
private ProgressIndicator progress;
#FXML
private ProgressIndicator stopProfInd;
#FXML
private BorderPane profilingpane;
#FXML
private Label devicename;
private ObservableValue<Number> profProperty;
// Reference to the main application.
private Main mainApp;
private AppIconsTask appLoadTask;
private ProfilingTask prTask;
private Queue<MyVbox> clickedAppQueue;
#Override // This method is called by the FXMLLoader when initialization is complete
public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
clickedAppQueue = new LinkedList<MyVbox>();
}
/**
* Is called by the main application to give a reference back to itself.
*
* #param mainApp
*/
public void setMainApp(Main mainApp) {
this.mainApp = mainApp;
}
/**
* Is called by the main application to give a reference back to itself.
*
* #param task
*
*/
public void setAppLoadTask(AppIconsTask task) {
this.appLoadTask = task;
progress.progressProperty().bind(appLoadTask.progressProperty());
progresspane.visibleProperty().bind(appLoadTask.runningProperty());
appLoadTask.setOnSucceeded(t -> drawAppIcons(appLoadTask.getApps()));
profilingpane.setVisible(false);
}
void drawAppIcons(ObservableList<App> apps){
progress.setVisible(false);
profilingpane.setVisible(true);
prTask = new ProfilingTask();
stopProfInd.progressProperty().bind(prTask.progressProperty());
new Thread(prTask).start();
}
}
Any clues?
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane prefHeight="263.0" prefWidth="304.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.gamebench.ios.controller.AppController">
<children>
<StackPane fx:id="stackpaneapps" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="10.0">
<children>
<BorderPane fx:id="profilingpane" prefHeight="200.0" prefWidth="200.0">
<center>
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" BorderPane.alignment="CENTER">
<children>
<ProgressIndicator fx:id="stopProfInd" />
</children>
</VBox>
</center>
</BorderPane>
<BorderPane fx:id="progresspane" prefHeight="200.0" prefWidth="200.0">
<center>
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" BorderPane.alignment="CENTER">
<children>
<ProgressIndicator fx:id="progress" maxHeight="100.0" maxWidth="100.0" prefHeight="68.0" prefWidth="77.0" />
</children>
</VBox>
</center>
</BorderPane>
</children>
</StackPane>
</children>
</AnchorPane>
Adding Task implementation as requested. The following is for appIconsTask which works well.
protected ObservableList<App> call() throws Exception {
String deviceId = "blah";
/* We will start before we get the list of installed apps */
updateProgress(0.0f , 100);
// Get list of installed apps
App[] installedApps = getAppsList(deviceId , false);
appList = FXCollections.observableList(new ArrayList<App>());
int numApps = installedApps.length;
double progress = 1.0f;
for(App app: installedApps){
byte[] pngData;
pngData = comm.getAppIcon(deviceId, app.getBundleId());
app.setAppIcon(pngData);
appList.add(app);
updateProgress((progress/numApps)*100, 100);
progress += 1.0f;
}
return appList;
}
Task implementation for the next progress indicator,
protected Void call() throws Exception {
double f = 1.0;
while(!getStopProf()){
Thread.sleep(30);
}
return null;
}
Reproduction of the issue here,
https://github.com/h-karthik/BugReproStackOverflow
I think you are running into this bug which, at the time of writing, is fixed in the latest GA release (JDK 8u20). The easiest fix, if it's possible for you to do, is to upgrade to and require that version.
If you can't do that, the next most intuitive option is to avoid use of setVisible(...) and just add the progress indicator to the scene graph when you need it. You can define elements in FXML which are not contained in the scene graph by using <fx:define>. Inject this as usual, and then just add the element to the scene graph as you need it. (You can remove it in similar fashion, if you need.)
Your FXML file becomes:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane prefHeight="263.0" prefWidth="304.0"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.stackoverflow.repro.controller.AppController">
<children>
<StackPane fx:id="stackpaneapps" AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0"
AnchorPane.topAnchor="10.0">
<children>
<BorderPane fx:id="progresspane" prefHeight="200.0"
prefWidth="200.0">
<center>
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0"
BorderPane.alignment="CENTER">
<children>
<ProgressIndicator fx:id="progress" maxHeight="100.0"
maxWidth="100.0" prefHeight="68.0" prefWidth="77.0" />
</children>
</VBox>
</center>
</BorderPane>
</children>
</StackPane>
</children>
<fx:define>
<BorderPane prefHeight="200.0" prefWidth="200.0" fx:id="profilingpane">
<center>
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0"
BorderPane.alignment="CENTER">
<children>
<ProgressIndicator fx:id="stopProfInd" />
</children>
</VBox>
</center>
</BorderPane>
</fx:define>
</AnchorPane>
and the controller becomes
package com.stackoverflow.repro.controller;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import com.stackoverflow.repro.Main;
import com.stackoverflow.repro.tasks.AppIconsTask;
import com.stackoverflow.repro.tasks.ProfilingTask;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.*;
public class AppController
implements Initializable {
#FXML
private StackPane stackpaneapps;
#FXML
private BorderPane progresspane;
#FXML
private ProgressIndicator progress;
#FXML
private ProgressIndicator stopProfInd;
#FXML
private BorderPane profilingpane;
// Reference to the main application.
private Main mainApp;
private AppIconsTask appLoadTask;
private ProfilingTask prTask;
#Override // This method is called by the FXMLLoader when initialization is complete
public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
}
/**
* Is called by the main application to give a reference back to itself.
*
* #param mainApp
*/
public void setMainApp(Main mainApp) {
this.mainApp = mainApp;
}
/**
* Is called by the main application to give a reference back to itself.
*
* #param task
*
*/
public void setAppLoadTask(AppIconsTask task) {
this.appLoadTask = task;
progress.progressProperty().bind(appLoadTask.progressProperty());
progresspane.visibleProperty().bind(appLoadTask.runningProperty());
// profilingpane.setVisible(false);
appLoadTask.setOnSucceeded(t -> drawAppIcons());
}
void drawAppIcons(){
// profilingpane.setVisible(true);
stackpaneapps.getChildren().add(profilingpane);
prTask = new ProfilingTask();
new Thread(prTask).start();
}
}
Your workaround with the opacity works as well, but this just feels like a more natural approach. There is a slight difference in functionality, which is not evident from everything being in a StackPane. With both setVisible (when it works correctly) and the setOpacity(...) workaround, the progress indicator will take up space in the layout. With adding and removing it when it is required, it will not take up space in the layout when it is not present. So the choice of approach probably depends on how you want it to behave in terms of layout.
Just a couple of other comments about your code:
If you make the thread running the prTask a daemon thread, it will not prevent the JVM from exiting when you close the last window. Obviously you may have other mechanisms for shutting this down in your real app, but it can be a useful trick (and is less annoying if you're testing this from Eclipse...):
Thread prThread = new Thread(prTask);
prThread.setDaemon(true);
prThread.start();
Also, your threading code in the profiling task looks like it is not quite correct. The stopProf field is almost certainly changed on a different thread to the one on which it is read. This means there is no guarantee of liveness of the field (there can be an arbitrary delay, possible indefinite, between it being changed in one thread and that change being visible in another). You should do one of the following:
Mark the field as volatile, or
Mark the getStopProf() and setStopProf(...) methods as synchronized, or
Replace the boolean field with an AtomicBoolean.
This third option is the one I prefer (favor high-level APIs over low-level primitives):
package com.stackoverflow.repro.tasks;
import java.util.concurrent.atomic.AtomicBoolean;
import javafx.concurrent.Task;
/**
* Created by karthik on 17/02/15.
*/
public class ProfilingTask extends Task<Void> {
AtomicBoolean stopProf;
#Override
protected Void call() throws Exception {
double f = 1.0;
while(!getStopProf()){
Thread.sleep(30);
}
return null;
}
public boolean getStopProf(){
return stopProf.get();
}
public void setStopProf(boolean stop){
stopProf.set(stop);
}
}
So, it turns out that it might be my ignorance or possibly a bug.
In my code instead of the following line
profilingpane.setVisible(true);
I use the following and the problem goes away. This is more of a hack to solve the problem.
profilingpane.setOpacity(0.0);
And then when I want the pane to show up,
profilingpane.setOpacity(1.0);

Show / Hide a Node within a stage

I am aware that there are already answers for this question but somehow, it was not able to solve my problem.
When I click the textfield in IMAGE1, I want the keyboard FXML(IMAGE2) to appear as is in IMAGE3. But the thing is, I can't seem to find the solution. How do I do this?
I need your help please.
IMAGE1
IMAGE2
IMAGE3
I am experimenting with something similar, here is what I came up with (one of many possible solutions):
You need to wrap your main layout (IMAGE1) in a StackPane. Its usage is to overlay what needs be overlaid (the keyboard in your case). The keyboard is placed in another pane (a VBox in the following example). The VBox:
is placed in the StackPane after the main layout to sit on top of it
is given a maximum height, so that it doesn't fill the entire window
is given a translateY equal to the height
and bottom alignment
The relevant code n FXML:
<VBox fx:id="statusContainer" maxHeight="100.0" prefHeight="100.0"
translateY="100.0" StackPane.alignment="BOTTOM_LEFT" />
This will always be outside of the view. Toggling the keyboard requires 2 TranslateTransitions (can it be done with 1, I wonder?), one to move the keyboard up, one down.
The example code:
1) Java:
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Test1 extends Application
{
#FXML private VBox statusContainer;
private TranslateTransition showStatus;
private TranslateTransition hideStatus;
boolean showsStatus = false;
#Override
public void start(Stage primaryStage) {
try {
StackPane page = (StackPane) FXMLLoader.load(this.getClass().getResource("test1.fxml"));
Scene scene = new Scene(page);
primaryStage.setTitle(this.getClass().getName());
primaryStage.setScene(scene);
primaryStage.sizeToScene();
primaryStage.show();
}
catch (IOException e) {
Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, e);
}
}
#FXML void initialize() {
showStatus = new TranslateTransition(Duration.millis(250), statusContainer);
showStatus.setByY(-100.0);
showStatus.setOnFinished(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
showsStatus = true;
}
});
hideStatus = new TranslateTransition(Duration.millis(250), statusContainer);
hideStatus.setByY(100.0);
hideStatus.setOnFinished(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
showsStatus = false;
}
});
}
public void toggleStatus() {
if( showsStatus ) {
showStatus.stop();
hideStatus.play();
}
else {
hideStatus.stop();
showStatus.play();
}
}
public static void main(String[] args) {
launch(args);
}
}
2) FXML (call it test1.fxml to match the code):
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<StackPane id="StackPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml" fx:controller="fancymsg.FancyMsg1">
<children>
<AnchorPane prefHeight="200.0" prefWidth="200.0">
<children>
<Button mnemonicParsing="false" onAction="#toggleStatus" text="Status" AnchorPane.leftAnchor="50.0" AnchorPane.topAnchor="100.0" />
</children>
</AnchorPane>
<VBox fx:id="statusContainer" maxHeight="100.0" prefHeight="100.0" translateY="100.0" StackPane.alignment="BOTTOM_LEFT" />
</children>
<stylesheets>
<URL value="#test1.css" />
</stylesheets>
</StackPane>
3) The CSS for the VBox to stand out (call it test1.css):
#statusContainer {
-fx-background-color: -fx-color;
}
An mcve version of this answer
can be found here

Resources