testFX.java :
public class testFX extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
try{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/testFX/view/test.fxml"));
System.out.println("after set location");
//PROBLEM
AnchorPane root = (AnchorPane)loader.load();
System.out.println("Does not happen");
testFXController listController = loader.getController();
listController.start();
Scene scene = new Scene(root, 200, 300);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
catch (Exception ex){
System.out.println("Error");
}
}
public static void main(String[] args) {
launch(args);
}
}
testFXController.java :
package testFX.view;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.ListView;
public class testFXController {
#FXML ListView<String> listView;
private ObservableList<String> obsList;
public void start() {
// create an ObservableList
// from an ArrayList
obsList = FXCollections.observableArrayList("Giants", "Patriots", "Jaguars");
listView.setItems(obsList);
}
}
test.fxml :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.ListView?>
<AnchorPane xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="view.testFXController">
<ListView fx:id="listView"
AnchorPane.topAnchor="10"
AnchorPane.leftAnchor="10"
AnchorPane.rightAnchor="10"
AnchorPane.bottomAnchor="10" />
</AnchorPane>
When I run the testFX.java, the system prints:
after set location
Error
This is the professor's code and I cannot seem to get it running. I realized that the main problem is in the line of code AnchorPane root = (AnchorPane)loader.load(); but I have no idea how to fix this, can someone help?
The value fx:controller attribute is most likely wrong (unless you have a different controller class than the one posted)
The controller you want to use: testFX.view.testFXController
Attribute value in the fxml: view.testFXController != testFX.view.testFXController
Assuming there is no other error that cannot be reproduced with the information in the question, fixing the attribute value should work.
The fx:controller attribute within the fxml file is the culprit here.
fx:controller="application.Controller" worked for me.
Therefore, fx:controller="testFX.view.testFXController" should work for you.
Related
Somehow, I cannot add items to my ComboBox variable from another class as it always says either the value of variable is null or the return value of its getter is null.
I use Scene Builder to build Sample.fxml, with controller as Controller class, luanch the whole thing form UserInterface class
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller">
<center>
<ComboBox fx:id="myBox" prefWidth="150.0" BorderPane.alignment="CENTER" />
</center>
</BorderPane>
Controller
public class Controller {
#FXML
private ComboBox<String> myBox;
public ComboBox<String> getMyBox() {
return myBox;
}
public void initialize() {
ObservableList<String> defaultTicker = FXCollections.observableArrayList("Something");
myBox.getItems().addAll(defaultTicker);
}
}
UserInterface
public class UserInterface extends Application {
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Sample.fxml"));
Parent root = loader.load();
Controller controller = new Controller();
primaryStage.setTitle("Title");
primaryStage.setScene(new Scene(root));
primaryStage.show();
List<String> addStuff = new ArrayList<String>();
addStuff.add("a");
addStuff.add("b");
addStuff.add("c");
controller.getMyBox().getItems().addAll(addStuff);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
After running, the interface appears, with only the option "Something" in it. So I guess it initialize fine?
The interface after launch
What did I do wrong? I couldn't seem to find any solutions so far.
Thanks in advance.
How would you implement a full screen feature that can be toggled by pressing F11?
You can add an EventHandler to the primaryStage where you specify the functionality like:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("View.fxml"));
AnchorPane pane = loader.load();
primaryStage.setScene(new Scene(pane, 400, 400));
primaryStage.addEventHandler(KeyEvent.KEY_PRESSED, event -> {
if (KeyCode.F11.equals(event.getCode())) {
primaryStage.setFullScreen(!primaryStage.isFullScreen());
}
});
primaryStage.show();
}
}
Just to be complete:
View.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="stackoverflow.testfullscreen.Controller">
</AnchorPane>
Controller:
public class Controller implements Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
I didn't implement a webview, but it should work with any scene.
Say I have a controller with
#FXML private ObservableList<String> myStrings = FXCollections.observableArrayList();
Is it possible to write any FXML which will wire up a ListView with myStrings as its items?
My first try was:
<ListView>
<items fx:id="myStrings"/>
</ListView>
But this complains that fx:id is not valid in that position. I also tried
<ListView items="${controller.myStrings}"/>
...but it couldn't resolve that value.
Please do not post this solution:
<ListView fx:id="myStringsListView"/>
// In controller
#FXML private ListView<String> myStringsListView;
#FXML public void initialize() {
myStringsListView.setItems(myStrings);
}
This is what I am doing now but the amount of indirection and boilerplate here hurts me.
The following works
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.ListView?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="ListViewController">
<center>
<ListView items="${controller.myStrings}" />
</center>
</BorderPane>
with the following controller (the main difference, I think, being that you either didn't define an accessor method for the list, or named it incorrectly):
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class ListViewController {
private final ObservableList<String> myStrings = FXCollections.observableArrayList();
public ListViewController() {
myStrings.addAll("One", "Two", "Three");
}
public ObservableList<String> getMyStrings() {
return myStrings ;
}
}
This quick test:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class ListViewItemsFromControllerTest extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setScene(new Scene(FXMLLoader.load(getClass().getResource("ListViewItemsFromController.fxml"))));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
produces
I want to have a collapsible list so I'm using a TreeView, but longer strings are giving horizontal scrollbars instead of word wrapping. I've tried using the CSS property -fx-wrap-text on the .tree-cell class but unfortunately nothing seems to happen.
Are TreeCells not meant to span more than one line? What alternative is there if that's the case?
Here's an image of what it looks like now:
test.css
.tree-cell {
-fx-wrap-text: true;
-fx-text-fill: blue;
}
subjects.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.net.URL?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication1.FXMLDocumentController">
<Button fx:id="button" layoutX="182.0" layoutY="14.0" onAction="#handleButtonAction" text="Click Me!" />
<TextField fx:id="filterSubject" layoutX="21.0" layoutY="14.0" />
<TreeView fx:id="subjectList" editable="true" layoutX="14.0" layoutY="61.0" prefHeight="200.0" prefWidth="365.0" />
<stylesheets>
<URL value="#test.css" />
</stylesheets>
</AnchorPane>
subjects.txt - just pairs of text where the first is subject, and second is description.
1
Somewhat long string that I want to wordwrap.
2
Somewhat long string that I want to wordwrap.
3
Somewhat long string that I want to wordwrap.
FXMLDocumentController.java
package javafxapplication1;
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.TreeView;
import javafx.scene.control.TreeItem;
import java.io.*;
public class FXMLDocumentController implements Initializable {
#FXML
private TreeView<String> subjectList;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
}
private void getSubjects(){
TreeItem<String> subjectRoot = new TreeItem<String> ("Subject");
subjectRoot.setExpanded(true);
try {
BufferedReader fileReader = new BufferedReader(
new FileReader("subjects.txt")
);
String subject = null;
String description = null;
while((subject = fileReader.readLine()) != null) {
description = fileReader.readLine();
TreeItem<String> subjectTree = new TreeItem<String> (subject);
TreeItem<String> descriptionItem = new TreeItem<String> (description);
subjectTree.getChildren().add(descriptionItem);
subjectRoot.getChildren().add(subjectTree);
}
} catch(FileNotFoundException e) {
System.out.println("File not found");
} catch(IOException e) {
e.printStackTrace();
}
subjectList.setRoot(subjectRoot);
}
#Override
public void initialize(URL url, ResourceBundle rb) {
getSubjects();
}
}
JavaFXApplication1.Java
package javafxapplication1;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class JavaFXApplication1 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();
}
public static void main(String[] args) {
launch(args);
}
}
Actually the CSS class is working as you expect.
The problem is: The TreeView contains TreeCells. These TreeCells will wrap its text, in case of they will have not enough vertical space to grow vertically. As the TreeView has a built in "ScrollPane" which ones view-port will grow vertically, it will provide enouth space for the TreeCell instances in any case to grow, therefore the wrapping will never be enabled.
To avoid this you could set the cell factory to generate the TreeCell instances manually. And then you can bind the prefWidthProperty of these elements to the widthProperty of the TreeView.
Example
public class WrappedTreeView extends Application {
#Override
public void start(Stage stage) {
final Scene scene = new Scene(new Group(), 200, 400);
Group sceneRoot = (Group) scene.getRoot();
scene.getStylesheets().add(getClass().getResource("application.css").toString());
TreeItem<String> root = new TreeItem<>("Root");
root.setExpanded(true);
TreeItem<String> childNode1 = new TreeItem<>("I am a very long node - the first one -, my text must be wrapped! If it is not wrapped, it's a problem!");
TreeItem<String> childNode2 = new TreeItem<>("I am a very long node - the second one -, my text must be wrapped! If it is not wrapped, it's a problem!");
TreeItem<String> childNode3 = new TreeItem<>("I am a very long node - the third one -, my text must be wrapped! If it is not wrapped, it's a problem!");
root.getChildren().addAll(childNode1, childNode2, childNode3);
childNode2.getChildren().add(new TreeItem<>("And I am a very long embedded node, so my text must be wrapped!"));
TreeView<String> treeView = new TreeView<>(root);
treeView.setCellFactory(item -> {
TreeCell<String> treeCell = new TreeCell<String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty)
setText(item);
else
setText("");
}
};
treeCell.prefWidthProperty().bind(treeView.widthProperty().subtract(5.0));
return treeCell;
});
treeView.setMaxWidth(200);
sceneRoot.getChildren().add(treeView);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
The content of application.css
.tree-cell {
-fx-wrap-text: true;
-fx-text-fill: blue;
}
And the generated TreeView:
You can do this by setting the tree cell's prefWidth value in a treecell cellfactory (along with the treecell's wraptext value to true), which will then stop the cell from expanding horizontally with a scrollPane.
EDIT 4
I've created a simple example that should give you an idea about what's happening right now.
What's happening right now is that whenever I click the button to print "HELLO WORLD" to the TextArea, the program will hang and use 100% of the CPU. There's also no output in the Eclipse console panel too.
Main.java
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("/application/test.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
MainController.java
public class MainController {
#FXML
private TextArea console;
private PrintStream ps = new PrintStream(new Console(console));
public void button(ActionEvent event) {
System.setOut(ps);
System.setErr(ps);
System.out.println("Hello World");
}
public class Console extends OutputStream {
private TextArea console;
public Console(TextArea console) {
this.console = console;
}
public void appendText(String valueOf) {
Platform.runLater(() -> console.appendText(valueOf));
}
public void write(int b) throws IOException {
appendText(String.valueOf((char)b));
}
}
}
EDIT 2: It seems that my question is way too long and hard to understand. I'm in the middle of restructuring this one.
EDIT 3
I guess I should just show everything here. What I'm trying to do is a simple GUI front-end for a CLI application. I'm a CS student and Java is our main language, so this is mainly for practice.
I've been looking every where for hours and hours but there's still no solution to this. I've tried doing the same like I did previously with Swing. The method worked fine with Swing but not with JavaFX.
Here's my (current) logger.java Class:
package application;
import java.io.*;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
public class ytdlLogger extends OutputStream implements Initializable
{
private TextArea loggerPane;
public ytdlLogger(TextArea loggerPane) {
this.loggerPane = loggerPane;
}
public void appendText(String valueOf) {
Platform.runLater(() -> loggerPane.appendText(valueOf));
}
#Override
public void initialize(URL location, ResourceBundle resources) {
OutputStream out = new OutputStream() {
#Override
public void write(int b) throws IOException {
appendText(String.valueOf((char)b));
}
};
System.setOut(new PrintStream(out, true));
System.setErr(new PrintStream(out, true));
}
#Override
public void write(int b) throws IOException {
// TODO Auto-generated method stub
}
}
I don't think there's any actual problems with this. I also did add the PrintStream object to redirect System.setOut and System.setErr in the MainController class to the TextArea, but it didn't work either.
I also have another Main class, which is the main thing that loads the FXML. I tried redirecting the output from there, it almost worked. Just almost, because i stopped seeing the console outputs inside Eclipse and I knew that was a great progress.
So, what seems to be the problem here? Is it because of the FXML? I'm absolute beginner in Java and also JavaFX, this is my first JavaFX application. Any guidance is very much appreciated. Thank you in advance.
EDIT 1
Here's the Main class:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("/application/Main.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
You are initializing ps with the value of console before it has been initialized by the FXMLLoader. I.e you have
#FXML
private TextArea console;
private PrintStream ps = new PrintStream(new Console(console));
Clearly console is still null when you pass it to new Console(...).
You need to initialize ps after the FXMLLoader has initialized the injected fields, which you can do using the initialize method.
SSCCE:
MainController.java:
package application;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
public class MainController {
#FXML
private TextArea console;
private PrintStream ps ;
public void initialize() {
ps = new PrintStream(new Console(console)) ;
}
public void button(ActionEvent event) {
System.setOut(ps);
System.setErr(ps);
System.out.println("Hello World");
}
public class Console extends OutputStream {
private TextArea console;
public Console(TextArea console) {
this.console = console;
}
public void appendText(String valueOf) {
Platform.runLater(() -> console.appendText(valueOf));
}
public void write(int b) throws IOException {
appendText(String.valueOf((char)b));
}
}
}
Main.java:
package application;
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) {
try {
Parent root = FXMLLoader.load(getClass().getResource("test.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
test.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<center>
<TextArea fx:id="console"/>
</center>
<bottom>
<Button onAction="#button" text="Output">
<BorderPane.alignment>CENTER</BorderPane.alignment>
<BorderPane.margin><Insets top="5" left="5" right="5" bottom="5"/></BorderPane.margin>
</Button>
</bottom>
</BorderPane>
You do not use your controller with an FXMLLoader. Otherwise you'd get an exception, since the class has no default constructor.
If you want to use the FXMLLoader to create your ytdlLogger, add the attribute fx:controller="application.ytdlLogger" (where fx is the fxml namespace prefix) to the root element of your fxml file.
If you want to do this, you also need to change some things:
ytdlLogger needs a default constructor (i.e. either remove your constructor or create a new one without arguments).
Add the #FXML annotation to your loggerPane field to allow the FXMLLoader to access that field to assign the TextArea with the fx:id="loggerPane" attribute to it.
better remove the base class OutputStream from the controller, since you don't use it.
Add some code that prints to System.out or System.err. Otherwise it's not surprising nothing is written to your TextArea. Make sure you do this after the controller is initialized.
Your controller should look like this after the changes:
public class ytdlLogger implements Initializable
{
#FXML
private TextArea loggerPane;
public void appendText(String valueOf) {
Platform.runLater(() -> loggerPane.appendText(valueOf));
}
#Override
public void initialize(URL location, ResourceBundle resources) {
OutputStream out = new OutputStream() {
#Override
public void write(int b) throws IOException {
appendText(String.valueOf((char)b));
}
};
System.setOut(new PrintStream(out, true));
System.setErr(new PrintStream(out, true));
}
}
And the fxml should look similar to this
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8"
fx:controller="application.ytdlLogger"> <!-- controller goes here -->
<children>
<TextArea fx:id="loggerPane" /> <!-- the TextArea you want to use for logging -->
</children>
</AnchorPane>