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);
Related
I'm worked with java for a year and am pretty new to javaFX and have been have been following a basic tutorial so far using scenebuilder. I've tried to apply a translatetransition to my button so far but it doesn't seem to move at all. When I run the program the scene with its background is displayed, but the button just stays in its defined start position in scenebuilder and won't move. After checking other similar questions on this site I've made sure that I implemented Initializable and adding #Override before my initialize function, and I've made sure my transition is played. I've tried the translatetransition on a rectangle too and it won't move. Might just be that I'm using eclipse and not netbeans
Driver Class:
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
//import javafx.scene.layout.BorderPane;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
Parent myroot = FXMLLoader.load(getClass().getClassLoader().getResource("MyFxml.fxml"));
//BorderPane root = new BorderPane();
Scene scene = new Scene(myroot);
//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);
}
}
FXML
<?import javafx.scene.shape.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<ImageView fitHeight="413.0" fitWidth="638.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../../../Downloads/campusmap.png" />
</image>
</ImageView>
<AnchorPane prefHeight="200.0" prefWidth="200.0">
<children>
<Rectangle fx:id="myrectangle" arcHeight="5.0" arcWidth="5.0" fill="#128cff" height="200.0" layoutX="14.0" layoutY="64.0" stroke="BLACK" strokeType="INSIDE" width="200.0" />
<Button fx:id="startbutton" layoutX="202.0" layoutY="232.0" mnemonicParsing="false" prefHeight="64.0" prefWidth="197.0" style="-fx-background-color: #FFC0CB; -fx-background-radius: 100;" text="Start Program">
<font>
<Font name="Comic Sans MS" size="12.0" />
</font></Button>
</children>
</AnchorPane>
</children>
</StackPane>
FXML Controller
package application;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;
import javafx.animation.TranslateTransition;
public class MyFxmlController implements Initializable{
#FXML
private Button startbutton;
#FXML
private Rectangle myrectangle;
#Override
public void initialize(URL url, ResourceBundle rb) {
TranslateTransition transition = new TranslateTransition();
transition.setDuration(Duration.seconds(4));
transition.setNode(startbutton);
transition.setToX(-200);
transition.setToY(-200);
transition.play();
}
}
You are not "linking" your controller to the FXML document. So when you display the FXML layout, the Transition code is never executed.
You have a couple of options to do this. In SceneBuilder, you can specify the controller class here:
This will add the fx:controller attribute to your FXML file:
fx:controller="temp.translate.MyFxmlController"
Obviously, you'll need to use your own package paths here.
The other option is to specify the controller in your Java code by updating your loading of the FXML document.
In order to do so, you'll need to get a reference to the FXMLLoader and set the controller there. You can change your start() method like this:
#Override
public void start(Stage primaryStage) {
try {
// Create a new FXMLLoader and set the FXML path
FXMLLoader loader = new FXMLLoader(getClass().getResource("MyFxml.fxml"));
// Set the controller for this FXML document
loader.setController(new MyFxmlController());
// Load the FXML into your Parent node
Parent myRoot = loader.load();
//BorderPane root = new BorderPane();
Scene scene = new Scene(myRoot);
//scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
Note: If you've specified the controller in your FXML via fx:controller, you cannot also specify it in your Java code, and vice versa. You may only define the controller in one place or the other, so it's really personal preference and depends on your needs.
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.
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…
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
Ok so I've created my own customer label, for this example it's extremely basic. What I did was extend the Javafx Label in my custom class called MyLabel. I'm also using FXML to create the GUI. Now when I do this I can't seem to instantiate with my custom class as I get this error.
"Can not set net.blacksquirreldevs.tests.MyLabel field net.blacksquirreldevs.tests.SampleLabelController.sampleLabel to javafx.scene.control.Label"
Here is the code for everything
Main.java
package net.blacksquirreldevs.tests;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("SampleLabel");
AnchorPane anchorPane = (AnchorPane) FXMLLoader.load(getClass().getResource("SampleLabel.fxml"));
primaryStage.setScene(new Scene(anchorPane));
primaryStage.show();
}
public static void main(String... args) {
launch(args);
}
}
MyLabel.java
package net.blacksquirreldevs.tests;
import javafx.scene.control.Label;
public class MyLabel extends Label {
public MyLabel(String text) {
super();
setText(text);
}
}
SampleLabelController.java
package net.blacksquirreldevs.tests;
import javafx.fxml.FXML;
import java.net.URL;
import java.util.ResourceBundle;
public class SampleLabelController {
#FXML
private ResourceBundle resources;
#FXML
private URL location;
#FXML
private MyLabel sampleLabel = new MyLabel("Hello world!");
#FXML
void initialize() {
assert sampleLabel != null : "fx:id=\"sampleLabel\" was not injected: check your FXML file 'SampleLabel.fxml'.";
}
}
And finally the fxml file SampleLabel.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.text.*?>
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml" fx:controller="net.blacksquirreldevs.tests.SampleLabelController">
<children>
<Label fx:id="sampleLabel" layoutX="153.0" layoutY="171.0" text="Sample Text">
<font>
<Font name="Arial Bold" size="50.0" />
</font>
</Label>
</children>
</AnchorPane>
So what I want to know is why I can' declare, in my SamplelabelController class, the sampleLabel as MyLabel?
Hopefully I've explained this well enough, if not let me know and I'll try to go a bit more in detail.
You are using <Label ... in your FXML instead of <MyLabel
Beside that your MyLabel has a none default constructor and in this case you NEED to provide a builder named MyLabelBuilder so that FXMLLoader can create an instance of MyLabel