I want to select a picture on the HDD with the help of JFileChooser and display it in the GUI. The problem is that I can't set the selected image to the ImageView. I think I'm doing something completely wrong but I also tried various other ways to display it for example with BufferedImage but nothing worked for me. I don't get behind this, please help me.
Controller:
package application;
import java.io.File;
import java.net.MalformedURLException;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileSystemView;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
public class WindowController {
#FXML private Button btn_load;
#FXML private TextField text_path;
#FXML private ImageView img_frame;
public Main main;
public void setMain(Main main) {
this.main = main;
}
#FXML
public void handle_load() throws MalformedURLException{
JFileChooser chooser = new JFileChooser(FileSystemView.getFileSystemView().getHomeDirectory());
int returnValue = chooser.showOpenDialog(null);
if (returnValue == JFileChooser.APPROVE_OPTION) {
text_path.setText(chooser.getSelectedFile().getAbsolutePath());
File file = new File(chooser.getSelectedFile().getAbsolutePath());
String localURL = file.toURI().toURL().toString();
img_frame.setImage(localURL);
}
}
}
And the fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="700.0" prefWidth="900.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.WindowController">
<children>
<ImageView fx:id="img_frame" fitHeight="516.0" fitWidth="627.0" layoutX="142.0" layoutY="114.0" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="97.0" AnchorPane.leftAnchor="142.0" AnchorPane.rightAnchor="142.0" AnchorPane.topAnchor="114.0" />
<HBox layoutX="14.0" layoutY="14.0" prefHeight="25.0" prefWidth="885.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="12.0">
<children>
<Button fx:id="btn_load" mnemonicParsing="false" onAction="#handle_load" text="Load Image" HBox.hgrow="NEVER" />
<TextField fx:id="text_path" prefHeight="25.0" prefWidth="0.0" HBox.hgrow="SOMETIMES" />
</children>
</HBox>
</children>
</AnchorPane>
instead of adding it to a url, just add
img_view.setImage(new Image(url));
It will fix your problem
Related
Below I have the code to launch my JavaFX GUI for a project I am working on. However when I go to execute the classes, it gets stuck. All it prints is Starting MCPY GUI and just sits there any help?
Launcher class
public class MCPY {
public static void main(String args[]) {
System.out.println("Starting MCPY GUI...");
Main.main(args);
}
}
Main
package com.mcpy.gui;
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 Exception{
FXMLLoader loader = new FXMLLoader(getClass().getResource("styles/Main.fxml"));
Parent root;
try {
root = loader.load();
} catch (IOException ioe) {
// log exception
return;
}
//init the connection to the controller class
MainController MainController = loader.getController();
MainController.setMain(this);
//load the scene
primaryStage.setTitle("Minecraft Launcher Python");
primaryStage.setScene(new Scene(root, 800, 500));
primaryStage.show();
};
public static void main(String[] args) {
launch(args);
}
Main.FXML
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.control.Button?>
<TitledPane animated="false" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" text="Minecraft Launcher Python Login Screen" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.mcpy.gui.MainController">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children>
<ImageView fitHeight="135.0" fitWidth="119.0" layoutX="14.0" layoutY="14.0" pickOnBounds="true" preserveRatio="true">
<image>
</image>
</ImageView>
<Button id="play" layoutX="231.0" layoutY="176.0" mnemonicParsing="false" onAction="#playButtonActionEvent" text="Play" />
<Button id="login" layoutX="313.0" layoutY="176.0" mnemonicParsing="false" onAction="#loginButtonEvent" text="Login" />
<Button id="create_account" layoutX="77.0" layoutY="176.0" mnemonicParsing="false" onAction="#create_accountButtonEvent" text="Create Account" />
</children></AnchorPane>
</content>
</TitledPane>
MainController
package com.mcpy.gui;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import com.mcpy.gui.Play;
import com.mcpy.gui.Login;
import com.mcpy.gui.CreateAccount;
/* Logic for the main gui. DO NOT EDIT. */
public class MainController {
#SuppressWarnings("unused")
private Main main;
public void setMain(Main main) {
this.main = main;
}
#FXML
private static Button login;
#FXML
private static Button create_account;
#FXML
private static Button play;
#FXML
public void loginButtonEvent(ActionEvent lbe) {
String[] args = (null);
Login.main(args);
};
#FXML
public void create_accountButtonEvent(ActionEvent cabe) {
String[] args = (null);
CreateAccount.main(args);
};
#FXML
public void playButtonActionEvent(ActionEvent pbae) {
Play.play_mc();
}
}
All of the code is here. The Launcher (MCPY.java) the main gui and its respecitve controller and the stylesheet (Main.java, MainController.java and Main.FXML)
EDIT: I have applied the FXML changes suggested by #naimdjon, however upon clicking the button this error is produced: https://pastebin.com/8EV9wfnm
You have multiple namespace declaration in your xml and imports were missing. Change your fxml to this (removed image for testing), this shows a window:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.control.Button?>
<TitledPane fx:controller="com.mcpy.gui.MainController" animated="false"
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"
minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0"
text="Minecraft Launcher Python Login Screen"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children>
<ImageView fitHeight="135.0" fitWidth="119.0" layoutX="14.0" layoutY="14.0" pickOnBounds="true" preserveRatio="true">
<image>
</image>
</ImageView>
<Button id="play" layoutX="231.0" layoutY="176.0" mnemonicParsing="false" text="Play" />
<Button id="login" layoutX="313.0" layoutY="176.0" mnemonicParsing="false" text="Login" />
<Button id="create_account" layoutX="77.0" layoutY="176.0" mnemonicParsing="false" text="Create Account" />
</children></AnchorPane>
</content>
</TitledPane>
UPDATE
Upon clicking on the button, you seem to be calling the launch() method. You can't call launch() method more than once in a Java FX application.
I have a fontawesomeicon. If I press the button btStart a red circle should flash on the Fontawesomeicon (see GIF). If I press the button btStop it should be disabled.
I tried it with a label.
When I press the button btStart, the label changes in a time interval with SetVisible(true) and setVisible(false).
Can someone offer me a solution? Thank you in advance.
Main.java
package sample;
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 Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller.java
package sample;
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import java.awt.*;
public class Controller {
#FXML
private FontAwesomeIconView iconCamera;
#FXML
private Button btStart;
#FXML
private Button btStop;
#FXML
void mouseCklicked(ActionEvent event) {
System.out.println("sad");
}
}
sample.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane alignment="center" hgap="10" vgap="10" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.141" fx:controller="sample.Controller">
<columnConstraints>
<ColumnConstraints />
</columnConstraints>
<rowConstraints>
<RowConstraints />
</rowConstraints>
<children>
<AnchorPane prefHeight="387.0" prefWidth="573.0">
<children>
<FontAwesomeIconView fx:id="iconCamera" glyphName="VIDEO_CAMERA" layoutX="130.0" layoutY="123.0" size="36" />
<Button layoutX="253.0" layoutY="93.0" mnemonicParsing="false" onAction="#mouseCklicked" text="Start" />
<Button layoutX="354.0" layoutY="93.0" mnemonicParsing="false" text="Stop" />
</children>
</AnchorPane>
</children>
</GridPane>
Create an Image (PNG) without the red dot.
Create a GIF with the red dot blinking effect.
Import both images in to your project.
On pressing the start set the image to *.GIF.
On pressing stop set the image to *.PNG.
Simple. Don't make things complicated!
When I start my application my transparent javafx.stage.Stage shows a half transparent image as expected. But after a second stage is loaded the first stage loses its transparency.
The weird thing is that if the second fxml file ("MainScreen.fxml") doesn't contains any components like buttons or text fields, the background stays transparent.
I'm using JavaFX with JavaSE-1.8 in eclipse neon.2 on macOS Sierra.
Main class
package customPackage.main;
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
public class Main extends Application implements Runnable {
private Stage primaryStage;
private Stage stage;
private Controller launchController;
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
launchController = new Controller("LaunchScreen.fxml");
Scene launchScene = new Scene(launchController);
launchScene.setFill(Color.TRANSPARENT);
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.setAlwaysOnTop(true);
primaryStage.setResizable(false);
primaryStage.setScene(launchScene);
primaryStage.show();
Thread thread = new Thread(this);
thread.start();
}
#Override
public void run() {
try {
Thread.sleep(3000);
// simulate work
} catch (InterruptedException e) {
e.printStackTrace();
}
Controller controller = new Controller("MainScreen.fxml");
Scene scene = new Scene(controller);
Platform.runLater(new Runnable() {
#Override
public void run() {
stage = new Stage();
stage.initStyle(StageStyle.UNDECORATED);
stage.setResizable(false);
stage.setScene(scene);
stage.show();
}
});
}
}
Controller class
package customPackage.main;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.AnchorPane;
public class LaunchController extends AnchorPane {
#FXML
private ProgressBar bar;
public LaunchController(String filename) {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(filename));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
}
FXML File ("LaunchScreen.fxml")
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<fx:root fx:id="pane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="326.0" prefWidth="883.0" type="AnchorPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<ImageView fitHeight="326.0" fitWidth="883.0" layoutX="7.0" layoutY="134.0" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<image>
<Image url="#http://www.lunapic.com/editor/premade/transparent.gif" />
</image>
</ImageView>
</children>
<cursor>
<Cursor fx:constant="DEFAULT" />
</cursor>
</fx:root>
FXML File ("MainScreen.fxml")
<?xml version="1.0" encoding="UTF-8"?>
<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<fx:root fx:id="pane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="720.0" prefWidth="1280.0" type="AnchorPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<ImageView fitHeight="720.0" fitWidth="1280.0" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<image>
<Image url="#http://i.telegraph.co.uk/multimedia/archive/03589/Wellcome_Image_Awa_3589699k.jpg" />
</image>
</ImageView>
<HBox spacing="100.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">
<children>
<Button fx:id="button1" mnemonicParsing="false" onAction="#button1Press" prefHeight="45.0" prefWidth="1000.0" text="Singleplayer" />
<Button fx:id="button2" mnemonicParsing="false" onAction="#button2Press" prefHeight="45.0" prefWidth="1000.0" text="Multiplayer" />
<Button fx:id="button3" mnemonicParsing="false" onAction="#button3Press" prefHeight="45.0" prefWidth="1000.0" text="Settings" />
<Button fx:id="button4" mnemonicParsing="false" onAction="#button4Press" prefHeight="45.0" prefWidth="1000.0" text="Quit" />
</children>
<padding>
<Insets bottom="30.0" left="100.0" right="100.0" top="20.0" />
</padding>
</HBox>
<Region fx:id="region" prefHeight="15.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</fx:root>
Solution (solved by James_D)
I added
.root {
-fx-background-color: transparent;
}
to the external style sheet and added stylesheets="#application.css" to the root node in the FXML file.
The default CSS style sheet for JavaFX, modena, applies a non-transparent color to the background of the root node. That's the color you're seeing when you display your main view.
The reason you don't see this in your first screen, or if you remove the buttons from the main screen, is that the default style sheet is only loaded the first time the Control class (or one of its subclasses) is instantiated. This is done to avoid the overhead of CSS processing for applications not needing it.
To fix, either add style="-fx-background-color: transparent;" to the root node in the FXML file, or add the rule
.root {
-fx-background-color: transparent ;
}
to the external style sheet.
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.