I'm creating a CSS stylesheet for a JavaFX 8 application.
I'm repeating these steps several times:
Edit the CSS
Run the application and see the changes
Stop the application
I found this approach very time-consuming and looking for something smarter.
Is it possibile to edit the CSS of my JavaFX application while running and see the result in real-time?
I'm looking for something like the Inspect tool of Google Chrome.
This can be partially reached by using Oracle Scene Builder. In fact I can load a CSS file and run the preview. Any change to the CSS file is correctly shown in the preview without the need to restart.
Unfortunately that's just a preview: many components are empty/uninitialized. Sure this is anyway very helpful, but many refinements require the real application running.
You will have to change using getStylesheets() / setStyle() etc.
In the example below I had to use a temp file as I am changing the CSS of the scene, when using into single components this is not necessary as you can change directly in the components.
You may download this example on gist.
Test.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Test extends Application{
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("doc.fxml"));
Parent root = (Parent)loader.load();
CSSController myController = loader.getController();
Scene scene = new Scene(root);
myController.setScene(scene);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
CSSController.java
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
public class CSSController implements Initializable{
private Scene scene;
#FXML TextArea cssContent;
#FXML Button defineCSSButton;
#Override
public void initialize(URL location, ResourceBundle resources) {
defineCSSButton.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event) {
String css = cssContent.getText();
try{
File temp = new File("tempfile.css");
temp.delete();
temp.createNewFile();
BufferedWriter bw = new BufferedWriter(new FileWriter(temp));
bw.write(css);
bw.close();
String url = "tempfile.css";
scene.getStylesheets().add(temp.toURI().toString());
}catch(Exception e){
e.printStackTrace();
}
}
});
}
public void setScene(Scene scene) { this.scene = scene; }
}
doc.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.shape.*?>
<AnchorPane 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" fx:controller="CSSController">
<children>
<TextArea fx:id="cssContent" layoutX="14.0" layoutY="26.0"
prefHeight="292.0" prefWidth="319.0" />
<Label layoutX="22.0" layoutY="6.0" text="Define your CSS here:" />
<Button fx:id="defineCSSButton" layoutX="14.1875" layoutY="333.5"
mnemonicParsing="false" text="Define CSS" />
<Line endY="400.0" layoutX="332.0" />
<Label layoutX="418.0" layoutY="15.0" text="Components:" />
<ChoiceBox layoutX="343.0" layoutY="48.0" prefWidth="150.0" />
<ColorPicker layoutX="343.3759765625" layoutY="86.0" />
<CheckBox layoutX="347.6650390625" layoutY="123.0"
mnemonicParsing="false" text="CheckBox" />
<MenuButton layoutX="343.37109375" layoutY="159.5"
mnemonicParsing="false" text="MenuButton">
<items>
<MenuItem mnemonicParsing="false" text="Action 1" />
<MenuItem mnemonicParsing="false" text="Unspecified Action" />
<MenuItem mnemonicParsing="false" text="Unspecified Action" />
<CheckMenuItem mnemonicParsing="false" text="Unspecified Action" />
</items>
</MenuButton>
<ProgressIndicator layoutX="347.9091796875" layoutY="200.01953125"
progress="0.0" />
<Slider layoutX="410.0" layoutY="213.01953125" />
<ToggleButton layoutX="348.7421875" layoutY="254.0"
mnemonicParsing="false" text="ToggleButton" />
</children>
</AnchorPane>
Css in the screenshot:
.label{
-fx-font: 15px "System Bold";
-fx-text-fill: #FF0000;
}
.button{
-fx-background-color:
#c3c4c4,
linear-gradient(#d6d6d6 50%, white 100%),
radial-gradient(center 50% -40%, radius 200%, #e6e6e6 45%, rgba(230,230,230,0) 50%);
-fx-background-radius: 30;
-fx-background-insets: 0,1,1;
-fx-text-fill: black;
-fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 3, 0.0 , 0 , 1 );
}
Related
I have tried researching various pages regarding how to switch scenes or even stages with no luck to help my specific case. I am trying to create a program with a login form that manages a school library.
I have a login form that authenticates the user input with the method validateLogin(). If the login is successful (i.e. isLoginSuccess = true), I want my program to close primaryStage and open a new stage/window with the library management system.
Is it better to switch the scene on primaryStage or close primaryStage and open a new stage? How do you do that and should you do that from Main.java or is it fine to do it from LoginController.java? I'm having trouble switching scenes or even just creating a new stage from LoginController.java since primaryStage isn't recognized in that class.
Main.java
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("login.fxml"));
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setScene(new Scene(root, 520, 400));
primaryStage.setResizable(false);
primaryStage.show();
}
//main() method is ignored in correctly deployed JavaFX application.
public static void main(String[] args)
{
launch(args);
}
}
LoginController.java
package sample;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.event.ActionEvent;
import javafx.stage.StageStyle;
import java.io.File;
import java.util.ResourceBundle;
//Can be removed, StackOverflow import
import java.io.BufferedReader;
import java.io.FileReader;
import java.net.URL;
public class LoginController implements Initializable
{
#FXML
private Button cancelButton;
#FXML
private Label loginMessageLabel;
#FXML
private ImageView brandingImageView;
#FXML
private ImageView lockImageView;
#FXML
private TextField usernameTextField;
#FXML
private PasswordField enterPasswordField;
#Override
public void initialize(URL url, ResourceBundle resourceBundle)
{
File brandingFile = new File("Images/LibManager.png");
Image brandingImage = new Image(brandingFile.toURI().toString());
brandingImageView.setImage(brandingImage);
File lockFile = new File("Images/LoginLock.png");
Image lockImage = new Image(lockFile.toURI().toString());
lockImageView.setImage(lockImage);
}
//Activates validateLogin() if there is input in text fields.
public void loginButtonOnAction(ActionEvent event)
{
if (usernameTextField.getText().isBlank() == false && enterPasswordField.getText().isBlank() == false)
{
validateLogin();
}
else
{
loginMessageLabel.setText("Please enter username and password");
}
}
//Closes login window.
public void cancelButtonOnAction(ActionEvent event)
{
Stage stage = (Stage) cancelButton.getScene().getWindow();
stage.close();
}
//Compares user input to txt file contents to authenticate.
public void validateLogin() {
{
try {
String location = "userdatabase.txt";
String username = usernameTextField.getText();
String password = enterPasswordField.getText();
FileReader fr = new FileReader(location);
BufferedReader br = new BufferedReader(fr);
String line, user, pass;
boolean isLoginSuccess = false;
while ((line = br.readLine()) != null) {
user = line.split(" ")[1].toLowerCase();
pass = line.split(" ")[2].toLowerCase();
if (user.equals(usernameTextField.getText()) && pass.equals(enterPasswordField.getText())) {
isLoginSuccess = true;
break;
}
}
if (!isLoginSuccess)
{
/*
public void startLibManager(Stage primaryStage) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("LibDatabase.fxml"));
Parent mainCallWindowFXML = loader.load();
secondaryStage.initStyle(StageStyle.UNDECORATED);
secondaryStage.setScene(new Scene(root, 520, 400));
secondaryStage.setResizable(false);
secondaryStage.show();
}
*/
/*
//use one of the components on your scene to get a reference to your scene object.
Stage stage = (Stage)tfCallerName.getScene.getWindow();//or use any other component in your controller
Scene mainCallWindow = new Scene (mainCallWindowFXML, 800, 600);
stage.setScene(newCallDetails);
stage.show(); //this line may be unnecessary since you are using the same stage.
*/
}
fr.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
login.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="520.0" style="-fx-background-color: #FFFFFF;" xmlns="http://javafx.com/javafx/15.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.LoginController">
<left>
<AnchorPane prefHeight="407.0" prefWidth="227.0" BorderPane.alignment="CENTER">
<children>
<ImageView fx:id="brandingImageView" fitHeight="400.0" fitWidth="226.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../../Images/LibManager.png" />
</image>
</ImageView>
</children></AnchorPane>
</left>
<right>
<AnchorPane prefHeight="400.0" prefWidth="332.0" style="-fx-background-color: FFFFFF;" BorderPane.alignment="CENTER">
<children>
<ImageView fx:id="lockImageView" fitHeight="32.0" fitWidth="46.0" layoutX="134.0" layoutY="65.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../../Images/LoginLock.png" />
</image>
</ImageView>
<Label layoutX="23.0" layoutY="157.0" prefHeight="17.0" prefWidth="60.0" text="Username" textFill="#01989e" />
<TextField fx:id="usernameTextField" layoutX="96.0" layoutY="152.0" prefWidth="173.0" promptText="Username" />
<Label layoutX="26.0" layoutY="205.0" text="Password" textFill="#01989e" />
<PasswordField fx:id="enterPasswordField" layoutX="96.0" layoutY="200.0" prefHeight="27.0" prefWidth="173.0" promptText="Password" />
<Button fx:id="loginButton" layoutX="22.0" layoutY="294.0" mnemonicParsing="false" onAction="#loginButtonOnAction" prefHeight="27.0" prefWidth="249.0" style="-fx-background-color: ff914d;" text="Login" textFill="WHITE" />
<Button fx:id="cancelButton" layoutX="22.0" layoutY="342.0" mnemonicParsing="false" onAction="#cancelButtonOnAction" prefHeight="27.0" prefWidth="249.0" style="-fx-background-color: ff914d;" text="Cancel" textFill="WHITE" />
<Label fx:id="loginMessageLabel" layoutX="26.0" layoutY="248.0" prefHeight="17.0" prefWidth="160.0" textFill="RED" />
</children></AnchorPane>
</right>
</BorderPane>
#Loritt I might be a bit late, but I use this method to change scenes when using JavaFX.
If you are going to be changing back and forth between scene, this method keeps you from having to write redundant code.
Stage stage;
Parent scene;
public void switchViews(ActionEvent event, String fileLocation) throws IOException {
stage = (Stage) ((Button) event.getSource()).getScene().getWindow();
scene = FXMLLoader.load(getClass().getResource(fileLocation));
stage.setScene(new Scene(scene));
stage.show();
}
I usually put this method inside the controller class which is completely acceptable as it has to do with the controlling of the visuals in the application and not any business logic.
Also, when I was originally learning how to do this myself and then pass information between the scenes I used this video and youtuber who has some good content on JavaFX/Scene Builder. (Link: https://www.youtube.com/watch?v=XCgcQTQCfJQ)
Hope this helps answer your question.
Happy coding! :)
I've developed an application using JavaFX that, in part, uses a WebView. However, I've noticed that when either by the user or programmatically via JS, the WebView is scrolled to the bottom or just near the bottom, strange things seem to happen. I am specifically hoping for some way to stop this behavior, and still need to allow scrolling in both senses. It's similar to this question but they are not the same. The rest of my post explains what is happening.
If you scroll to the bottom and resize the window to be larger than previously and hover over other JavaFX controls in the window, they become white boxes.
To illustrate, suppose you have the following window:
Scroll to the bottom and resize the window to be larger than previously and then hover over the JavaFX button and TextBox, who both disappear when you do so:
This happens on at least Windows 10 with Java8u111 and Java8u51. I've made an example to demonstrate this:
example.Main.java
package example;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("Example.fxml"));
BorderPane rootWindow = (BorderPane) loader.load();
Scene scene = new Scene(rootWindow);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
example.ExampleController.java
package example;
import javafx.concurrent.Worker;
import javafx.fxml.FXML;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
public class ExampleController {
#FXML private WebView webView;
#FXML
private void initialize() {
WebEngine engine = webView.getEngine();
engine.load("https://stackoverflow.com");
engine.getLoadWorker().stateProperty().addListener( (o, oldVal, newVal) -> {
if (newVal == Worker.State.SUCCEEDED) {
engine.executeScript("window.scrollTo(0, document.body.scrollHeight);");
}
});
}
}
example.Example.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.web.WebView?>
<BorderPane prefHeight="400.0" prefWidth="500.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.111" fx:controller="example.ExampleController">
<center>
<WebView fx:id="webView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</BorderPane.margin>
</WebView>
</center>
<bottom>
<HBox BorderPane.alignment="CENTER">
<children>
<TextField HBox.hgrow="ALWAYS" />
<Button alignment="TOP_LEFT" mnemonicParsing="false" text="Button" />
</children>
</HBox>
</bottom>
</BorderPane>
Is there a way I can stop this from happening or at least mitigate it?
I also encountered the same problem, and I found a solution:
Add a parameter to jvm:-Dprism.order=sw
see:https://news.kynosarges.org/2016/04/09/javafx-and-java_tool_options/?tdsourcetag=s_pctim_aiomsg
i used scene builder to generate a layout, after exporting the fxml i imported into TextPad, the layout was sucessefuly imported however i cant handle the items by the id(if thats how it works). my question is how do handle the items that i added.
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;
import javafx.scene.Parent;
import javafx.fxml.FXMLLoader;
public class test2fx extends Application{
public static void main(String[] args){
Application.launch(args);
}
public void init(){
}
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("fxlayout.fxml"));
Scene scene = new Scene(root, 300, 275);
stage.setTitle("FXML Welcome");
stage.setScene(scene);
stage.show();
}
public void stop(){
System.exit(0);
}
fxml file content:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.Pane?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="284.0" prefWidth="314.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Button layoutX="31.0" layoutY="252.0" mnemonicParsing="false" prefHeight="25.0" prefWidth="50.0" text="Jogar" />
<Button layoutX="124.0" layoutY="252.0" mnemonicParsing="false" prefHeight="25.0" prefWidth="50.0" text="Novo" />
<Button layoutX="219.0" layoutY="252.0" mnemonicParsing="false" prefHeight="25.0" prefWidth="50.0" text="Sair" />
<TextField id="tf1" disable="true" layoutX="150.0" layoutY="60.0" />
<TextField id="tf1" disable="true" layoutX="150.0" layoutY="100.0" />
<TextField id="tf3" layoutX="150.0" layoutY="140.0" />
<TextField id="tf4" disable="true" layoutX="150.0" layoutY="180.0" />
<Label id="lb1" layoutX="38.0" layoutY="60.0" prefHeight="22.0" prefWidth="59.0" text="Inicio" />
<Label id="lb2" layoutX="38.0" layoutY="100.0" prefHeight="22.0" prefWidth="59.0" text="Fim" />
<Label id="lb3" layoutX="38.0" layoutY="139.0" prefHeight="22.0" prefWidth="59.0" text="Palpite" />
<Label id="lb4" layoutX="38.0" layoutY="180.0" prefHeight="22.0" prefWidth="59.0" text="Inicio" />
</children>
</Pane>
As #James_D said you most likely want:
A reference to the element in your FXMl you wish to access.
A controller class for your FXML.
Example:
/*
* Dean2191 Stackoverflow example
*/
package javafxapplication6;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
/**
*
* #author dean2191
*/
public class FXMLDocumentController implements Initializable {
#FXML
private Label label;
#FXML
private TextField tf1; // value will be injected by the FXMLLoader
#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
}
}
Check the oracle examples here and also note that the netbeans IDE generates a default controller class as shown above however when a new element is added in scene builder you should add each #FXML annotation to the variable you wish to access and scene builder/netbeans have synchronization features to view which elements are referenced from your code.
Synchronizing With the Controller Source Code The NetBeans IDE's Make
Controller feature allows you to synchronize the modifications you
make in the FXML file that is currently opened in Scene Builder and
the controller source code opened in NetBeans IDE. To illustrate this
feature, do the following:
In Scene Builder, drag a Button control from the Library panel to the
Control panel. In the Code panel, assign a new value for the new
button's fx:id field and a new method name for the onAction method.
Select File in the main menu and then Save. In NetBeans IDE 7.4 or
later, right click the project node for the FXML file that you just
edited and select Edit from the contextual menu. From the main menu,
select Source and then Make Controller. The #FXML private variable and
the new onAction method for the button you just added in Scene Builder
are created in the controller source file. Use the Make Controller
command if you delete an element in the Control panel or update an
fx:id value or a method name in Scene Builder.
Source
Also note you should check in your FXMl for this line:
fx:controller="yourNamespace.fxlayout"
As your controller class must be specified somewhere in the FXML. Your controller will then be called something like fxlayout.java in your example code. This can be set/modified using scene builder too:
How do i center this image horizontally? I've tried putting in a HBox like so, however it does not work...
<HBox alignment="CENTER">
<ImageView fitWidth="300" preserveRatio="true">
<image>
<Image url="#Logo.png"/>
</image>
</ImageView>
</HBox>
Here a little MCVE with a FXML file:
Application:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author Naoghuman
*/
public class CenterImageInHBox 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);
}
}
FXML file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="center.image.in.hbox.FXMLDocumentController">
<children>
<VBox layoutX="60.0" prefHeight="200.0" prefWidth="100.0" spacing="7.0" style="-fx-background-color: YELLOWGREEN;" AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="14.0">
<children>
<ToolBar prefHeight="40.0" prefWidth="200.0">
<items>
<Button mnemonicParsing="false" onAction="#onActionLeft" text="Left" />
<Button mnemonicParsing="false" onAction="#onActionCenter" text="Center" />
<Button mnemonicParsing="false" onAction="#onActionRight" text="Right" />
</items>
</ToolBar>
<HBox fx:id="hbImage" alignment="TOP_CENTER" VBox.vgrow="ALWAYS">
<children>
<ImageView fx:id="ivImage" fitHeight="128.0" fitWidth="128.0" pickOnBounds="true" preserveRatio="true" />
</children>
</HBox>
</children>
</VBox>
</children>
</AnchorPane>
and the Controller:
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
/**
*
* #author Naoghuman
*/
public class FXMLDocumentController implements Initializable {
#FXML private HBox hbImage;
#FXML private ImageView ivImage;
#Override
public void initialize(URL url, ResourceBundle rb) {
Image img = new Image("http://icons.iconarchive.com/icons/evermor-design/galaxian/128/Movie-Clip-icon.png");
ivImage.setImage(img);
}
public void onActionCenter() {
hbImage.setAlignment(Pos.TOP_CENTER);
}
public void onActionLeft() {
hbImage.setAlignment(Pos.TOP_LEFT);
}
public void onActionRight() {
hbImage.setAlignment(Pos.TOP_RIGHT);
}
}
When you click on the buttons in the application then you will see that the image is aligned left, center or right depends from your the choosen action.
Well, my question is simple.
I am building an application with Java Fx, and I have a TabPane.
When I open a new Tab, that Tab gets it's content by a fxml file.
tab.setContent((Node)FXMLLoader.load(this.getClass().getResource("/main/textEditor.fxml")))
Fine, that works well, it always loads the same file on all Tabs, so, all textarea on all tabs have the same id.
The problem is, with java, I can only get the information of the textarea of the first Tab.
How can i edit specifically the textarea of one tab in particular?
An example of what i want to do :
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class Main extends Application{
public static void main(String[] args) {
Application.launch(Main.class, args);
}
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/tabPane/test.fxml"));
Screen screen = Screen.getPrimary();
Rectangle2D bounds = screen.getVisualBounds();
stage.setScene(new Scene(root));
stage.show();
}
}
test.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.net.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.layout.AnchorPane?>
<VBox prefHeight="400.0" prefWidth="640.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tabPane.controller">
<children>
<MenuBar VBox.vgrow="NEVER">
<menus>
<Menu mnemonicParsing="false" onAction="#test" text="File">
<items>
<MenuItem fx:id="insert" mnemonicParsing="false" onAction="#test" text="Insert" />
</items>
</Menu>
</menus>
</MenuBar>
<AnchorPane maxHeight="-1.0" maxWidth="-1.0" prefHeight="-1.0" prefWidth="-1.0" VBox.vgrow="ALWAYS">
<children>
<Label alignment="CENTER" layoutX="155.0" layoutY="177.0" style="
" text="Drag components from Library here…" textAlignment="CENTER" textFill="#9f9f9f" wrapText="false">
<font>
<Font size="18.0" />
</font>
</Label>
<TabPane fx:id="tabPane" prefHeight="375.0" prefWidth="640.0" tabClosingPolicy="UNAVAILABLE">
<tabs>
<Tab text="Untitled Tab 1">
<content>
<TextArea fx:id="textarea" prefHeight="200.0" prefWidth="200.0" text="a" />
</content>
</Tab>
<Tab text="Untitled Tab 2">
<content>
<TextArea fx:id="textarea1" prefHeight="200.0" prefWidth="200.0" text="a" />
</content>
</Tab>
</tabs>
</TabPane>
</children>
</AnchorPane>
</children>
<stylesheets>
<URL value="#../../../../BasicApplicatio11n_css/BasicApplication.css" />
controller.java
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextArea;
public class controller implements Initializable{
#FXML
private TextArea textarea;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
}
public void test(ActionEvent event){
textarea.appendText("Text");
}
}
There are two tabs on this example, when the button is pressed, I want to add the text on the current selected tab.
There are a few different ways to do this. One way is to let the controller for the tab content expose the textProperty from the text area. Then in your "main controller", create a StringProperty field. When you create a new tab, just observe its selected property and update the string property field to point to the one from the current controller. Then you can easily load text into the "current" pane.
Here's a simple example of this:
EditorController:
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
public class EditorController {
#FXML
private TextArea textArea ;
public StringProperty textProperty() {
return textArea.textProperty() ;
}
}
with textEditor.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TextArea?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="EditorController">
<center>
<TextArea fx:id="textArea"/>
</center>
</BorderPane>
And then the MainController could look like:
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.stage.FileChooser;
public class MainController {
#FXML
private TabPane tabPane ;
private StringProperty currentText ;
public void initialize() throws IOException {
// load an initial tab:
newTab();
}
#FXML
private void newTab() throws IOException {
Tab tab = new Tab("Untitled text");
FXMLLoader loader = new FXMLLoader(getClass().getResource("textEditor.fxml"));
tab.setContent(loader.load());
EditorController controller = loader.getController() ;
tab.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
currentText = controller.textProperty();
}
});
tabPane.getTabs().add(tab);
tabPane.getSelectionModel().select(tab);
}
#FXML
private void load() {
FileChooser chooser = new FileChooser();
File file = chooser.showOpenDialog(tabPane.getScene().getWindow());
if (file != null) {
Path path = file.toPath();
try {
currentText.set(String.join("\n", Files.readAllLines(path)));
} catch (IOException e) {
Alert alert = new Alert(AlertType.ERROR);
alert.setContentText("Unable to load file "+path);
alert.setTitle("Load error");
alert.showAndWait();
}
tabPane.getSelectionModel().getSelectedItem().setText(path.getFileName().toString());
}
}
}
with main.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="MainController">
<center>
<TabPane fx:id="tabPane"/>
</center>
<bottom>
<HBox alignment="CENTER" spacing="5">
<padding>
<Insets top="5" right="5" left="5" bottom="5" />
</padding>
<Button text="Load..." onAction="#load" />
<Button text="New" onAction="#newTab"/>
</HBox>
</bottom>
</BorderPane>
For completeness, a simple application class:
import java.io.IOException;
import javafx.application.Application;
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 {
Parent root = FXMLLoader.load(getClass().getResource("main.fxml"));
Scene scene = new Scene(root, 800, 800);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
There are other approaches, e.g. you could just change the button's onAction property when the tab selection changes, etc.