How to write conditions on button click in JavaFX - java

I want to make it so that when a button is pressed, different windows are displayed, and for this I need conditions. I don't want to create many methods for each button
This code doesn't work:
#Override
public void buttonOnAction(ActionEvent event){
if(btnReaders.isPressed()){
btnReaders.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
Parent parent = null;
try {
parent = FXMLLoader.load(getClass().getResource("readersMenu.fxml"));
} catch (IOException ex) {
ex.printStackTrace();
}
Scene scene = new Scene(parent);
Stage window = (Stage) ((Node)event.getSource()).getScene().getWindow();
window.setScene(scene);
window.show();
}
});
}
else if(btnDashboard.isPressed()){
btnDashboard.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
Parent parent = null;
try {
parent = FXMLLoader.load(getClass().getResource("librarianMenu.fxml"));
} catch (IOException ex) {
ex.printStackTrace();
}
Scene scene = new Scene(parent);
Stage window = (Stage) ((Node)event.getSource()).getScene().getWindow();
window.setScene(scene);
window.show();
}
});
}
}

Here is an example of a parameterized event handler that will open the selected FXML in a new scene that will be set for the same stage containing the source node of the event.
When the event handler is created, the application stores, in the event handler, the name of the FXML resource to be loaded.
The event handler is assigned to a button action.
When the button is actioned, the event handler loads a new FXML into a new scene and attaches that scene to the window that the button is defined in.
Example App
For this example, FXML files should be in the same location as the package containing the SceneSelector application.
SceneSelector.java
import javafx.application.Application;
import javafx.event.*;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.Button;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.Objects;
public class SceneSelector extends Application {
#Override
public void start(Stage stage) {
Button sceneAButton = new Button("Scene A");
sceneAButton.setOnAction(
new SceneChangeEventHandler(
"sceneA.fxml"
)
);
Button sceneBButton = new Button("Scene B");
sceneBButton.setOnAction(
new SceneChangeEventHandler(
"sceneB.fxml"
)
);
Pane layout = new HBox(10,
sceneAButton,
sceneBButton
);
layout.setPadding(new Insets(10));
layout.setPrefSize(200, 150);
stage.setScene(
new Scene(layout)
);
stage.show();
}
class SceneChangeEventHandler implements EventHandler<ActionEvent> {
private final String fxmlResourceName;
public SceneChangeEventHandler(String fxmlResourceName) {
this.fxmlResourceName = fxmlResourceName;
}
#Override
public void handle(ActionEvent event) {
try {
Stage stage = (Stage) ((Node) event.getSource())
.getScene()
.getWindow();
changeScene(stage, fxmlResourceName);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void changeScene(
Stage stage,
String fxmlResourceName
) throws IOException {
Parent parent = FXMLLoader.load(
Objects.requireNonNull(
getClass().getResource(
fxmlResourceName
)
)
);
Scene scene = new Scene(parent);
stage.setScene(scene);
stage.setTitle(fxmlResourceName);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
sceneA.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<StackPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
prefHeight="150.0" prefWidth="200.0" style="-fx-background-color: lemonchiffon;"/>
sceneB.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<StackPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
prefHeight="150.0" prefWidth="200.0" style="-fx-background-color: azure;"/>

Related

Bind window visibility with running state of a service in JavaFX

I'm using a loading screen in my application while a service is running a task in another thread.
After the service is done, I would like to close the loading screen.
But instead of calling something like window.hide() every time, I would like to have a binding between the service state and the visibility of the window.
Service runs --> loading screen visible
Service runs not --> loading screen invisible
The service has properties like onRunningProperty() or runningProperty() and the window has onShownProperty() or showingProperty() but I didn't manage to bind them.
How can I bind the visibility of the loading screen with the running state of a service, so that the loading screen is automatically shown, when the service runs and hidden, when the service is done?
Example:
HelloApplication.java
public class HelloApplication extends Application {
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
HelloController.java
public class HelloController {
#FXML
private Label welcomeText;
private final Service<Void> service = new Service<>() {
#Override
protected Task<Void> createTask() {
return new Task<>() {
#Override
protected Void call() throws Exception {
Thread.sleep(3000);
return null;
}
};
}
};
public HelloController() {
service.setOnSucceeded(e -> {
// now I want to hide the loading screen
WaitController.waitController.waitLabel.getScene().getWindow().hide();
});
}
#FXML
protected void onHelloButtonClick() {
welcomeText.setText("Welcome to JavaFX Application!");
service.restart();
try {
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("wait.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 630, 400);
Stage stage = new Stage();
stage.setTitle("New Window");
stage.setScene(scene);
stage.show();
WaitController.waitController = fxmlLoader.getController();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
WaitController.java
public class WaitController {
#FXML
Label waitLabel;
public static WaitController waitController;
}
wait.fxml
<AnchorPane prefHeight="291.0" prefWidth="428.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.test.WaitController">
<children>
<Label fx:id="waitLabel" layoutX="161.0" layoutY="100.0" prefHeight="92.0" prefWidth="105.0" text="Wait..." textAlignment="CENTER">
<font>
<Font size="18.0" />
</font>
</Label>
</children>
</AnchorPane>
hello-view.fxml
<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml"
fx:controller="com.example.test.HelloController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
</padding>
<Label fx:id="welcomeText"/>
<Button text="Hello!" onAction="#onHelloButtonClick"/>
</VBox>
You won't be able to use bindings for this. There are no writable properties of Window that control whether or not it's showing. There is, of course, the showing property, but it is read-only. In other words, there's no appropriate property of Window that you can bind to the service's running property.
What you can do, however, is listen to the service's running property and call show() / hide() on the window instance as appropriate. For example:
import javafx.application.Application;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
var service = new ServiceImpl();
setupWindowForService(primaryStage, service);
var button = new Button("Start service");
button.disableProperty().bind(service.runningProperty());
button.setOnAction(e -> {
e.consume();
service.restart();
});
primaryStage.setScene(new Scene(new StackPane(button), 600, 400));
primaryStage.show();
}
private void setupWindowForService(Window owner, Service<?> service) {
var window = new Stage();
window.initOwner(owner);
window.initModality(Modality.WINDOW_MODAL);
window.setTitle("Service Window");
window.setScene(new Scene(new StackPane(new Label("Service running...")), 300, 150));
window.setOnCloseRequest(e -> {
if (service.isRunning()) {
// prevents user from closing the window while service is
// running. Perhaps it would make more sense to cancel the
// service?
e.consume();
}
});
// the code that shows and hides the window based on the service's state
service.runningProperty().addListener((obs, wasRunning, isRunning) -> {
if (isRunning) {
window.show();
} else {
window.hide();
}
});
}
private static class ServiceImpl extends Service<Void> {
#Override protected Task<Void> createTask() {
return new Task<>() {
#Override protected Void call() throws Exception {
int max = 3_000;
for (int i = 0; i < max; i++) {
Thread.sleep(1L);
}
return null;
}
};
}
}
}

JavaFX - switching views while maintaining toolbar

I want to create an app with a couple of different views (therefore I can change roots), however with the same toolbar (which after clicking certain buttons changes the root). So far I tried both hard-coding toolbar in fxtml files, as well as creating it in my main app (but the issue then is that I need to change scenes which don't contain toolbar any more). Right now my code looks like this:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
Parent bikeScene = FXMLLoader.load(getClass().getResource("bikes.fxml"));
Button bikes = new Button("Bikes");
bikes.getStylesheets().add("tool-bar");
bikes.setOnAction(e->primaryStage.setScene(new Scene(bikeScene, 900, 1900)));
ToolBar tb = new ToolBar();
tb.getItems().add(bikes);
primaryStage.setTitle("management app");
VBox vb = new VBox(tb);
Scene sc = new Scene(vb, 900, 1900);
sc.getStylesheets().add("sample/style.css");
primaryStage.setScene(sc);
primaryStage.setMaximized(true);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I know this is very bad, but I am pretty stuck and not sure how to incorporate both the root and the toolbar when creating a scene.
edit:
So I decided to have a mainView as my root and now I have:
public class Main extends Application {
public void start(Stage primaryStage) throws Exception{
Button bikes = new Button("Bikes");
bikes.getStyleClass().add("tool-bar");
Button rooms = new Button("Rooms");
rooms.getStyleClass().add("tool-bar");
Button food = new Button("Food");
food.getStyleClass().add("tool-bar");
Button cars = new Button("Cars");
cars.getStyleClass().add("tool-bar");
Button admin = new Button("Admin");
admin.getStyleClass().add("tool-bar");
BorderPane mainView = new BorderPane();
BorderPane ToolBorderPane = new BorderPane();
ToolBar tBarLeft=new ToolBar();
ToolBar tBarRight=new ToolBar();
tBarLeft.getItems().addAll(rooms, bikes, food, cars);
tBarRight.getItems().add(admin);
ToolBorderPane.setLeft(tBarLeft);
ToolBorderPane.setRight(tBarRight);
ToolBorderPane.getStyleClass().add("tool-bar");
mainView.setTop(ToolBorderPane);
bikes.setOnAction(e-> {
try {
mainView.getChildren().add(loader("bikes.fxml"));
} catch (IOException ex) {
ex.printStackTrace();
}
});
rooms.setOnAction(e-> {
try {
mainView.getChildren().add(loader("rooms.fxml"));
} catch (IOException ex) {
ex.printStackTrace();
}
});
food.setOnAction(e-> {
try {
mainView.getChildren().add(loader("food.fxml"));
} catch (IOException ex) {
ex.printStackTrace();
}
});
cars.setOnAction(e-> {
try {
mainView.getChildren().add(loader("cars.fxml"));
} catch (IOException ex) {
ex.printStackTrace();
}
});
admin.setOnAction(e->
{
try {
mainView.getChildren().add(loader("admin.fxml"));
} catch (IOException ex) {
ex.printStackTrace();
}
});
GridPane loader = loader("sample.fxml");
mainView.getChildren().add(loader);
primaryStage.setTitle("management app");
Scene sc = new Scene(mainView, 1900, 1900);
sc.getStylesheets().add("sample/style.css");
primaryStage.setScene(sc);
primaryStage.setMaximized(true);
primaryStage.show();
}
public GridPane loader(String string) throws IOException {
GridPane loader = FXMLLoader.load(getClass().getResource(string));
return loader;
}
public static void main(String[] args) {
launch(args);
}
}
In theory it somehow works, however after clicking on certain buttons I get really weirdly formatted content.
For instance, my rooms.fxml file looks as follows:
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
<Label alignment="BOTTOM_RIGHT" text="let's see if it works" />
</GridPane>
While the text appears on the left upper corner instead of bottom right.

JavaFx Change Window, Without New Controller Constructor

I have 2 fxml file. Example A.fxml. B.fxml. I have 2 controller. AController (A.fxml) BController (B.fxml). A fxml and B fxml have change button changing Scene or fxml. This is button code;
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/infoLibrary/view/A.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root);
Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow();
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
Same code in BContrroller change button. When i click change button scane changing. But everytime init method and controller contructers working too. When user change fxml javafx everytime using new constructor. How can i change windows without new controller constructor?
Use a view model:
public class ViewModel {
public enum View {A, B}
private final ObjectProperty<View> currentView = new SimpleObjectProperty<>(View.A);
public ObjectProperty<View> currentViewProperty() {
return currentView ;
}
public final View getCurrentView() {
return currentViewProperty().get();
}
public final View setCurrentView(View view) {
currentViewProperty().set(view);
}
}
Now in your AController do:
public class AController {
private ViewModel viewModel ;
public void setViewModel(ViewModel viewModel) {
this.viewModel = viewModel ;
}
// button handler:
#FXML
private void goToB(ActionEvent event) {
viewModel.setCurrentView(ViewModel.View.B);
}
}
and BController is similar.
Finally, you set everything up with something like the following, which is executed only once (e.g. in your start() method, or somewhere similar):
Stage stage = ... ; // maybe it's the primary stage in start...
Scene scene = new Scene();
ViewModel viewModel = new ViewModel();
FXMLLoader aLoader = new FXMLLoader(getClass().getResource("/infoLibrary/view/A.fxml"));
Parent a = aLoader.load();
AController aController = aLoader.getController();
aController.setViewModel(viewModel);
FXMLLoader bLoader = new FXMLLoader(getClass().getResource("/infoLibrary/view/B.fxml"));
Parent b = bLoader.load();
BController bController = bLoader.getController();
bController.setViewModel(viewModel);
scene.rootProperty().bind(Bindings.createObjectBinding(() -> {
if (viewModel.getCurrentView() == ViewModel.View.A) {
return a ;
} else if (viewModel.getCurrentView() == ViewModel.View.B) {
return b ;
} else {
return null ;
}
}, viewModel.currentViewProperty());
stage.setScene(scene);
stage.show();
Now the two FXML files are only loaded once (so the controllers are only created once, and their initialize() methods called only once). The switching is managed by changing the state of the ViewModel and observing that state, so the scene's root is changed when the model state changes.
Here is a complete SSCCE:
ViewModel.java:
package sceneswitcher;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
public class ViewModel {
public enum View {A, B}
private final ObjectProperty<View> currentView = new SimpleObjectProperty<>(View.A);
public ObjectProperty<View> currentViewProperty() {
return currentView ;
}
public final View getCurrentView() {
return currentViewProperty().get();
}
public final void setCurrentView(View view) {
currentViewProperty().set(view);
}
}
AController.java:
package sceneswitcher;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
public class AController {
private ViewModel viewModel ;
#FXML
private TextField textField ;
public void setViewModel(ViewModel viewModel) {
this.viewModel = viewModel ;
}
// button handler:
#FXML
private void goToB(ActionEvent event) {
viewModel.setCurrentView(ViewModel.View.B);
}
public String getText() {
return textField.getText();
}
}
A.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.Button?>
<VBox fx:controller="sceneswitcher.AController" spacing="5" alignment="CENTER"
xmlns:fx="http://javafx.com/fxml/1">
<Label text='This is view A'/>
<TextField fx:id="textField" />
<Button onAction="#goToB" text="Go to view B"/>
</VBox>
BController.java:
package sceneswitcher;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
public class BController {
private ViewModel viewModel ;
#FXML
private TextArea textArea ;
public void setViewModel(ViewModel viewModel) {
this.viewModel = viewModel ;
}
// button handler:
#FXML
private void goToA(ActionEvent event) {
viewModel.setCurrentView(ViewModel.View.A);
}
public String getText() {
return textArea.getText();
}
}
B.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.Button?>
<VBox fx:controller="sceneswitcher.BController" spacing="5" alignment="CENTER"
xmlns:fx="http://javafx.com/fxml/1">
<Label text="This is view B"/>
<TextArea fx:id="textArea" />
<Button onAction="#goToA" text="Go to View A"/>
</VBox>
Main.java:
package sceneswitcher;
import java.io.IOException;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws IOException {
ViewModel viewModel = new ViewModel();
FXMLLoader aLoader = new FXMLLoader(getClass().getResource("A.fxml"));
Parent a = aLoader.load();
AController aController = aLoader.getController();
aController.setViewModel(viewModel);
FXMLLoader bLoader = new FXMLLoader(getClass().getResource("B.fxml"));
Parent b = bLoader.load();
BController bController = bLoader.getController();
bController.setViewModel(viewModel);
Scene scene = new Scene(a, 400, 400);
scene.rootProperty().bind(Bindings.createObjectBinding(() -> {
if (viewModel.getCurrentView() == ViewModel.View.A) {
return a ;
} else if (viewModel.getCurrentView() == ViewModel.View.B) {
return b ;
} else {
return null ;
}
}, viewModel.currentViewProperty()));
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Merge multiple FXML and have one controller for each file

The best article I found was: How to create multiple javafx controllers with different fxml files?
However im really confused on how this works. All examples just seem a bit too complex for the initial learning.
So here I have a simple helloWorld for testing purposes. As you can see in the xml, I have a container, menu and footer. However, I want all 3 of them to have seperate controllers and XML files which are then merged and shown as seen in the XML below after the class:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class HelloWorld extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
FXMLLoader loader = new FXMLLoader();
Parent root = loader.setLocation(getClass().getResource("main.fxml"));
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
MainController mainController = loader.getController();
}
}
XML
<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.canvas.*?>
<HBox fx:id="container" id="container" fx:controller="core.GuiController" xmlns:fx="http://javafx.com/fxml">
<HBox fx:id="post" id="post">
<!-- Stuff -->
</HBox>
<HBox fx:id="friends" id="friends">
<!-- Stuff -->
</HBox>
<HBox fx:id="profile" id="profile">
<!-- Stuff -->
</HBox>
</HBox>
I could really benefit from a simple example. How can I keep them in seperate files and merge them while they each retain their own controllers?
You could follow this tutorial
public class MainApp extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("AddressApp");
initRootLayout();
showPersonOverview();
}
/**
* Initializes the root layout.
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Shows the person overview inside the root layout.
*/
public void showPersonOverview() {
try {
// Load person overview.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/PersonOverview.fxml"));
AnchorPane personOverview = (AnchorPane) loader.load();
// Set person overview into the center of root layout.
rootLayout.setCenter(personOverview);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Returns the main stage.
* #return
*/
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
In this example You have two fxml files, RootLayout.fxml and PersonOverview.fxml.
You set the scene of your primarystage to (BorderPane)RootLayout.fxml then add PersonOverview.fxml to the BorderPane.

JavaFX TitledPane lookup(.title) returns null

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);
}
}

Categories