I want to make switching photo program, using imageView and keyEvent, but when I try to use keyEvent it dosen't work, I was trying add scene.getRoot().requestFocus(); but didn't help, so my question is, why it dosen't work any how could I activate it?
MenuScreenController.java
#FXML
public void getNewImageHandler(KeyEvent event) {
System.out.println(singleFile.getName());
imgFieldView.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.N) {
photoSwipCounter++;
System.out.println("P clicked");
} else if (e.getCode() == KeyCode.P) {
photoSwipCounter--;
}
});
// if (event.getCode().equals(KeyCode.N)) {
// photoSwipCounter++;
// }
// if (event.getCode().equals(KeyCode.P)) {
// photoSwipCounter--;
// }
if (photoSwipCounter < 0) {
singleFile = selectedImgsList.get(selectedImgsList.size() - photoSwipCounter);
} else {
singleFile = selectedImgsList.get(photoSwipCounter);
}
image = new Image(singleFile.toURI().toString(),
900, 400,
true, true, true);
imgFieldView.setImage(image);
}
As you can see I was trying many methods, by stream, or classic, but it dosen't make change.
MenuScreen.fxml
<Pane fx:id="menuPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="screensaverfxml.Controllers.MenuScreenController">
<children>
<ImageView fx:id="imgFieldView" fitHeight="400.0" fitWidth="900.0" layoutX="66.0" layoutY="62.0" onKeyPressed="#getNewImageHandler" onMouseClicked="#imgDoubleClick" pickOnBounds="true" preserveRatio="true" />
</children>
</Pane>
ScreenSaverFXML.java
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(this.getClass().getResource("/screensaverfxml/fxmlConfig/MainScreen.fxml"));
Pane mainPane = loader.load();
Scene scene = new Scene(mainPane, 1000, 700);
primaryStage.setScene(scene);
primaryStage.setTitle("Screen Saver");
primaryStage.show();
scene.getRoot().requestFocus();
}
And ofc I have method to load first photo on imageView and it's work by load it from the folder to the list, but switching to next nope
Only nodes that have the focus receive key events. Since you have scene.getRoot().requestFocus() it's the root that receives the events. However, you're adding your EventHandler to imgFieldView and since that node is a descendant of the root it never sees any key events. You need to either have your imgFieldView obtain the focus or add your handler to the root node instead.
Note: You can only request focus once the target Node is part of a Scene.
The other problem is your call to imgFieldView.setOnKeyPressed(...) in the getNewImageHandler method. When you add an event handler via FXML, for instance by using onKeyPressed="#getNewImageHandler", it sets the corresponding onXXX property of the node. In your case, it's setting the onKeyPressed property of imgFieldView. However, in that handler you also set that property which replaces the handler set by the FXMLLoader. Your new handler simply increments or decrements the photoSwipeCounter field but does nothing with the new value. Your method should look more like:
#FXML
public void getNewImageHandler(KeyEvent event) {
if (event.getCode().equals(KeyCode.N)) {
photoSwipeCounter++;
}
if (event.getCode().equals(KeyCode.P)) {
photoSwipeCounter--;
}
// wrap around logic
if (photoSwipeCounter < 0) {
photoSwipeCounter = selectedImgsList.size() - 1;
} else if (photoSwipeCounter >= selectedImgsList.size()) {
photoSwipeCounter = 0;
}
singleFile = selectedImgsList.get(photoSwipeCounter);
image = new Image(singleFile.toURI().toString(),
900, 400,
true, true, true);
imgFieldView.setImage(image);
}
Related
In my JavaFX application I have to load many fxml files (200+) in the same time. I have decided to load them in background Task just like in https://stackoverflow.com/a/34878843 answear. Everything works fine (load time was acceptable) until JDK update. Newest version of JDK lengthened load time 3-4 times.
I have checked previous JDK releases and that problem appears from the JDK 8u92.
To test that issue I created new simple JavaFX FXML Application in Netbeans 8.1 and use only generated classes and fxml. Creating view from code works fine.
Application class:
public class FXMLLoaderTest extends Application {
private static Executor ex = Executors.newCachedThreadPool();
//private static Executor ex = Executors.newFixedThreadPool(400);
//private static Executor ex = Executors.newSingleThreadExecutor();
#Override
public void start(Stage stage) throws Exception {
VBox box = new VBox();
ScrollPane root = new ScrollPane(box);
Button b = new Button("GENERATE");
b.setOnAction(e -> {
IntStream.range(0, 1000).forEach(i -> {
Task<Parent> task = new Task<Parent>() {
#Override
protected Parent call() throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
Parent root = null;
try {
root = loader.load();
} catch (IOException ex) {
Logger.getLogger(Loader.class.getName()).log(Level.SEVERE, null, ex);
}
// StackPane root= new StackPane();
// Button click = new Button("Click");
// root.setPrefSize(300, 300);
// root.getChildren().add(click);
return root;
}
};
task.setOnSucceeded(ev -> {
final Parent parent = task.getValue();
box.getChildren().add(parent);
});
task.setOnFailed(ev -> task.getException().printStackTrace());
ex.execute(task);
});
});
box.getChildren().add(b);
Scene scene = new Scene(root, 400, 500);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
FXMLDocument.fxml
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fxmlloader.FXMLDocumentController">
<children>
<Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
<Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
</children>
</AnchorPane>
FXMLDocumentController.java
public class FXMLDocumentController implements Initializable {
#FXML
private Label label;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
label.setText("Hello World!");
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
I have tested this on several computers and result was always the same. On JDK 8u91 fxml files load fast. I have checked release note of 8u92 and I haven't found any changes in FXMLLoader class.
Has anybody encounter this issue? Mayby I am doing something wrong then please correct me.
i want to design a GUI that contains BorderPane in the top of the container there is a MenuBar, in the left of the container there is a Acordion with different buttons that change the content of the center of the container for diferent FXML files, someting like the desktop app of Spotiffy
i got a working prototipe and it looks like this
the change in FXML happens and the buttos respont very well, the problem i have is that the FXML that fill the center part of the BoderPane doesn't auto rize and if is to big parts of the FXML doesn't show and if the FXML is smaller that the space left for the center part stays of the same small size and left alot of space with nothing
this is my code for the calling of the new FXML
public void lanzaUno(){
try {
// load first FXML
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Coordinador.class.getResource(
"VistaControlador/Usuario/Uno.fxml"));
/*i put the AnchorPane inside of a
ScrollPane for the desire funcionality of alot
of vertical space for many nodes in a single FXML file
*/
ScrollPane unoAnchorPane = (ScrollPane) loader.load();
UnoController controller = loader.getController();
//example method for passing variables to the FXML controler
controller.pasoPrincipal(this, primaryStage, "Rhutt");
//puts the FXML in the center of the BorderPane
rootLayout.setCenter(unoAnchorPane);
//this if for trying to accommodate the content en the BorderPane
BorderPane.setAlignment(unoAnchorPane, Pos.TOP_LEFT);
} catch (IOException e) {
e.printStackTrace();
}
}
my first cuestion is what is the thing i am missing in the calling of the FXML for the contents of this ocupy the space available in the BorderPane?
my secon cuestion regards the change of FXML, when i pass fron one to another the change in the BorderPane is in a instant and looks very bad is there a way for making the transicion like one where the content of a FXML that is call push the content of the FXML in the center?, it doesn't have to be very elaborated just make the transition a little better
EDIT
i got a cordinator class where i send and recive parameters for all the FXML and where i declare the methods of calling new FXML, so i have a cordinator class, a FXML root with its controller and two FXML and its controllers with different things in each one, this two FXML are the ones that change in the BorderPane center of the root
this is the coordinator class
//Variables
private Stage primaryStage;
private BorderPane rootLayout;
/**
* launh
* #param primaryStage
*
*/
#Override
public void start(Stage primaryStage) throws Exception{
// Inicializa la escena
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Login");
this.primaryStage.centerOnScreen();
//star method
iniLogin();
}
/**
*load the root scene
*/
public void iniLogin(){
try {
// Carga el loader.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(com.aohys.rehabSys.MVC.Coordinador.class.getResource(
"VistaControlador/Pricipal/Principal.fxml"));
rootLayout = (BorderPane) loader.load();
//the root scene
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
// Da acceso al programa principal.
PrincipalController controller = loader.getController();
controller.pasoPrincipal(this, primaryStage);
primaryStage.centerOnScreen();
// Muesta la escena,
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
after this method there are two identical methods like the one at the beginning where i call the 2 changing FXML call LanzaUno, LanzaDos
this is my rood FXML controler
public class PrincipalController implements Initializable {
//variable of the coordinator class
private Coordinador cordi;
private Stage stage;
/**
* method for passing parameters to the FXML
* #param cordi
* #param stage
*/
public void pasoPrincipal(Coordinador cordi, Stage stage) {
this.cordi = cordi;
this.stage = stage;
}
//FXML in root
#FXML private Button btt1;
#FXML private Button btt2;
#FXML public static StackPane stackPane;
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
//Call the firts FXML
btt1.setOnAction((evento)->{
cordi.lanzaUno();
});
//Call the second FXML
btt2.setOnAction((evento)->{
cordi.lanzaDos();
});
}
for the moment the controllers on the two FXML dont do anything
You should read up on Adam Bien's AfterburnerFX library for managing dependency injection and controllers in your application. It has changed my life. :)
For transitions, I use this code. It is a nice fade-out/fade-in from one screen to the next. In this case, stackPane is a StackPane in the center of your BorderPane defined in your FXML.
Here is a simple FXML with the aforementioned stackPane:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<BorderPane fx:id="borderPane" minHeight="-Infinity" minWidth="-Infinity" prefHeight="768.0" prefWidth="1280.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.tada.gui.tada.TadaPresenter">
<center>
<StackPane fx:id="stackPane" minHeight="-Infinity" minWidth="-Infinity" prefHeight="240.0" prefWidth="320.0" BorderPane.alignment="CENTER" />
</center>
</BorderPane>
And the setScreen method that changes to the new one passed in:
/**
* Set Screen
*
* #param view
* #return boolean
*/
public boolean setScreen(Parent view) {
final DoubleProperty opacity = stackPane.opacityProperty();
if (!stackPane.getChildren().isEmpty()) { //if there is more than one screen
Timeline fade = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(opacity, 1.0)),
new KeyFrame(new Duration(TRANSITION_TIMER), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
stackPane.getChildren().remove(0); //remove the displayed screen
stackPane.getChildren().add(0, view); //add the screen
Timeline fadeIn = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(opacity, 0.0)),
new KeyFrame(new Duration(TRANSITION_TIMER), new KeyValue(opacity, 1.0)));
fadeIn.play();
}
}, new KeyValue(opacity, 0.0)));
fade.play();
} else {
stackPane.setOpacity(0.0);
stackPane.getChildren().add(view); //no one else been displayed, then just show
Timeline fadeIn = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(opacity, 0.0)),
new KeyFrame(new Duration(TRANSITION_TIMER), new KeyValue(opacity, 1.0)));
fadeIn.play();
}
return true;
}
You'll also need this in your controller...
private static final double TRANSITION_TIMER = 200;
EDIT:
I tried to put together a very basic "application". It is rudimentary, but I think it does a good job of illustrating the use of AfterburnerFX and the screen fade transition. There is a lot more to AfterburnerFX. I just used the view switching without the dependency injection, which is very important when you start wanting to work on objects in your application. Also, property binding is very important for a good UX. Anyway, Here is a link to my example on GitHub.
I'm making some kind of information application about a city which I need to use a database for, I got my database set up and ready and most of the code needed as well; the only part is to extract the data I need from my SQL server into a BarChart in fxml. Can anyone give me a example or something?
My fxml:
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controllers.ControllerZW">
<BarChart fx:id="graph" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<xAxis>
<CategoryAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</BarChart>
</Pane>
My controller atm:
package Controllers;
public class ControllerZW implements Interface.Ibacktomenu {
#Override
public void backtomenu() {
try {
Main.mainStage.setScene(
new Scene(FXMLLoader.load(getClass().getResource("../scenes/MainMenu.fxml")))
);
} catch (IOException e) {
System.out.println("Probably couldnt find resource file");
e.printStackTrace();
}
}
}
Do you want to render the data form database to XML format?
I think some template engine maybe help you, such as Freemarker.
You could use Freemarker as view of Spring MVC, please refer to http://viralpatel.net/blogs/spring-mvc-freemarker-ftl-example/
Use a Task to get the data from the database and any method described in Passing Parameters JavaFX FXML to get the data to the controller, e.g.
#Override
public void start(Stage primaryStage) {
Task<Pane> dataLoader = new Task<Pane>() {
#Override
protected Pane call() throws Exception {
// create data
XYChart.Series<String, Double> rain = new XYChart.Series<>();
rain.setName("rain");
Month[] months = Month.values();
for (Month month : months) {
// simulates slow database connection; just adding some random values here
Thread.sleep(200);
rain.getData().add(new XYChart.Data<>(month.toString(), Math.random() * 100));
}
// load chart (replace BarChartLoad.class.getResource("chart.fxml") with your own URL)
FXMLLoader loader = new FXMLLoader(BarChartLoad.class.getResource("chart.fxml"));
Pane pane = loader.load();
// pass data to controller
loader.<ControllerZW>getController().setData(FXCollections.observableArrayList(rain));
return pane;
}
};
// placeholder
Pane root = new Pane();
Scene scene = new Scene(root, 1000, 600);
// set new scene root on successfull completion of the task
dataLoader.setOnSucceeded(evt -> scene.setRoot(dataLoader.getValue()));
// TODO: handle task failure
new Thread(dataLoader).start();
primaryStage.setScene(scene);
primaryStage.show();
}
public class ControllerZW implements Interface.Ibacktomenu {
#FXML
private BarChart<String, Double> graph;
public void setData(ObservableList<XYChart.Series<String, Double>> data) {
graph.setData(data);
}
...
I am new to FXML and I am trying to create a handler for all of the button clicks using a switch. However, in order to do so, I need to get the elements using and id. I have tried the following but for some reason (maybe because I am doing it in the controller class and not on the main) I get a stack overflow exception.
public class ViewController {
public Button exitBtn;
public ViewController() throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("mainWindow.fxml"));
Scene scene = new Scene(root);
exitBtn = (Button) scene.lookup("#exitBtn");
}
}
So how will I get an element (for example a button) using it's id as a reference?
The fxml block for the button is:
<Button fx:id="exitBtn" contentDisplay="CENTER" mnemonicParsing="false"
onAction="#handleButtonClick" text="Exit" HBox.hgrow="NEVER" HBox.margin="$x1"/>
Use a controller class, so that you don't need to use a lookup. The FXMLLoader will inject the fields into the controller for you. The injection is guaranteed to happen before the initialize() method (if you have one) is called
public class ViewController {
#FXML
private Button exitBtn ;
#FXML
private Button openBtn ;
public void initialize() {
// initialization here, if needed...
}
#FXML
private void handleButtonClick(ActionEvent event) {
// I really don't recommend using a single handler like this,
// but it will work
if (event.getSource() == exitBtn) {
exitBtn.getScene().getWindow().hide();
} else if (event.getSource() == openBtn) {
// do open action...
}
// etc...
}
}
Specify the controller class in the root element of your FXML:
<!-- imports etc... -->
<SomePane xmlns="..." fx:controller="my.package.ViewController">
<!-- ... -->
<Button fx:id="exitBtn" contentDisplay="CENTER" mnemonicParsing="false" onAction="#handleButtonClick" text="Exit" HBox.hgrow="NEVER" HBox.margin="$x1" />
<Button fx:id="openBtn" contentDisplay="CENTER" mnemonicParsing="false" onAction="#handleButtonClick" text="Open" HBox.hgrow="NEVER" HBox.margin="$x1" />
</SomePane>
Finally, load the FXML from a class other than your controller class (maybe, but not necessarily, your Application class) with
Parent root = FXMLLoader.load(getClass().getResource("path/to/fxml"));
Scene scene = new Scene(root);
// etc...
I'm new to Java FX and am creating an application for fun. I'm trying to add a TitledPane dynamically and am getting Null Pointer Exceptions when attempting to lookup the title of the TitledPane about 70% of the time. I tried to create a simple demo of my issue, but was unable to reproduce the issue outside of my application, but I could solve my issue. I'm hoping someone could help me understand why my solution works and maybe point me in the direction of a better solution. I'm using an FXML file with a Controller. I'm attempting to lookup the title inside of Platform.runLater() because I'm manually editing the layout and elements of the title. Inside of the Controller's initialize function, I do the following to get null pointer exceptions:
Platform.runLater(new Runnable() {
#Override
public void run() {
titledpane.lookup(".title"); // will return null about 70% of the time
}
});
// Do more stuff
However, if I wrap that call in a timer and execute it in 500 ms, it doesn't seem to ever return Null.
new java.util.Timer().schedule(new java.util.TimerTask() {
#Override
public void run() {
Platform.runLater(new Runnable() {
#Override
public void run() {
titledpane.lookup(".title"); // doesn't seem to return null
}
});
}
}, 500);
One forum mentioned that CSS had to be applied to the element prior to calling a lookup on the title, so I tried manually applying CSS to the title, but titledpane.lookup(".title") still returned null. Can anyone help me understand what is happening here? Thanks in advance!
I had the same issue. I resolved it by calling applyCss() and layout() on the pane that contains the TitledPane:
Node loadedPane = paneLoader.load();
bodyPane.setCenter(loadedPane);
bodyPane.applyCss();
bodyPane.layout();
You should read the article Creating a Custom Control with FXML.
Here's an example about how you can load a TitledPane dynamically. A TitledPane is added each time you click the "Add Task" button.
Task.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<fx:root collapsible="false" prefHeight="72.0" prefWidth="202.0" text="Task" type="TitledPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<content>
<Pane prefHeight="43.0" prefWidth="200.0">
<children>
</children>
</Pane>
</content>
</fx:root>
Task.java
import java.io.IOException;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.Region;
public class Task extends Region {
TitledPane titledPane;
public Task( String title) {
final FXMLLoader fxmlLoader = new FXMLLoader( getClass().getResource( "Task.fxml"));
titledPane = new TitledPane();
fxmlLoader.setRoot( titledPane);
fxmlLoader.setController( this);
try {
fxmlLoader.load();
} catch( IOException exception) {
throw new RuntimeException( exception);
}
titledPane.setText(title);
getChildren().add( titledPane);
}
}
Demo.java
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
public class Demo extends Application {
#Override
public void start(Stage primaryStage) {
Group root = new Group();
Button addTaskButton = new Button( "Add Task");
addTaskButton.setOnAction(new EventHandler<ActionEvent>() {
double x=0;
double y=0;
int count=0;
#Override
public void handle(ActionEvent event) {
// calculate new position
x+=50;
y+=50;
// task counter
count++;
Task task = new Task( "Task " + count);
task.relocate(x, y);
root.getChildren().add( task);
}
});
root.getChildren().add( addTaskButton);
Scene scene = new Scene( root, 1024, 768);
primaryStage.setScene( scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Screenshot:
There are various solutions in order to create a custom title. Here's one. Note: You need to provide an icon in the proper path or adapt the path. Alternatively you can just disable the ImageView node and instead use the Rectangle for demonstration purposes. It's just another node that's displayed.
public class Task extends Region {
TitledPane titledPane;
public Task( String title) {
final FXMLLoader fxmlLoader = new FXMLLoader( getClass().getResource( "Task.fxml"));
titledPane = new TitledPane();
fxmlLoader.setRoot( titledPane);
fxmlLoader.setController( this);
try {
fxmlLoader.load();
} catch( IOException exception) {
throw new RuntimeException( exception);
}
// create custom title with text left and icon right
AnchorPane anchorpane = new AnchorPane();
double offsetRight = 20; // just an arbitrary value. usually the offset from the left of the title
anchorpane.prefWidthProperty().bind(titledPane.widthProperty().subtract( offsetRight));
// create text for title
Label label = new Label( title);
// create icon for title
Image image = new Image( getClass().getResource( "title.png").toExternalForm());
ImageView icon = new ImageView();
icon.setImage(image);
// Rectangle icon = new Rectangle(16, 16);
// set text and icon positions
AnchorPane.setLeftAnchor(label, 0.0);
AnchorPane.setRightAnchor(icon, 0.0);
// add text and icon to custom title
anchorpane.getChildren().addAll( label, icon);
// set custom title
titledPane.setGraphic( anchorpane);
// show only our custom title, don't show the text of titlePane
titledPane.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
getChildren().add( titledPane);
}
}