FXMLLoader.getController() returns null no matter what - java

I have been doing a small app for my studies in JavaFX. In the app, I need to get the controller separately from the Main file, so I've devised this:
public class DiceObserver implements Observer {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
public Controller controller = loader.<Controller>getController();
Dice ov;
public DiceObserver(Dice dice){
this.ov = dice;
}
public void update(Observable o, Object arg) {
int roll = ov.getLastValue().getRolledValue();
System.out.println(roll);
controller.updateBarChart(roll);
}
}
The FXML file:
package sample;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller{
#FXML public TextField freqText;
#FXML public Slider freqSlider;
#FXML public Button startButton;
#FXML public Button endButton;
#FXML public BarChart rollChart;
#FXML public ImageView dicePic;
XYChart.Series s1 = new XYChart.Series<>();
XYChart.Series s2 = new XYChart.Series<>();
XYChart.Series s3 = new XYChart.Series<>();
XYChart.Series s4 = new XYChart.Series<>();
XYChart.Series s5 = new XYChart.Series<>();
XYChart.Series s6 = new XYChart.Series<>();
Image side1 = new Image("Dice_Side_1.png");
SimulationThread t = SimulationThread.getInstance();
public void startSimulation(ActionEvent event) { // start the simulation
t.start();
}
public void getNewFreqValue(ActionEvent event) {
freqText.setText(String.valueOf(freqSlider.getValue()));
}
public void stopSimulation(ActionEvent event) {
t.end();
}
public void updateBarChart(int diceRoll){
System.out.println(diceRoll);
int rollval = diceRoll;
switch (rollval) {
case 1:
s1.getData().add(rollval);
break;
case 2:
s2.getData().add(rollval);
break;
case 3:
s3.getData().add(rollval);
break;
case 4:
s4.getData().add(rollval);
break;
case 5:
s5.getData().add(rollval);
break;
case 6:
s6.getData().add(rollval);
break;
}
}
public void updateDicePic(int diceRoll){
dicePic.setImage(side1);
}
public void initialize() {
}
}
The updateBarChart method is in the controller. When I run the code, the loader has a value, but the getcontroller method returns null. How can I fix this?

It returns null because you have not specified a controller as-of yet. From the code you posted above, your controller and FXML are not aware of each other.
Call:
loader.setController(yourControllerInstance);
Example:
https://github.com/SnakeDoc/superD/blob/master/src/com/vanomaly/superd/Main.java#L39
Or you must specify the controller in your FXML document (in your root element):
<AnchorPane maxHeight="...".... fx:controller="sample.Controller">
<HBox .... >
....
Example:
https://github.com/SnakeDoc/Narvaro/blob/master/src/resources/Narvaro.fxml#L13

FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
public Controller controller = loader.<Controller>getController();
You neither set the controller, nor can a controller class specified in the fxml file (fx:controller attribute) be initialized, since you do not call load() before using getController().
I guess you're trying to do the latter. However without actually loading the fxml file, no controller will be created. Just creating the FXMLLoader instance won't load anything. The input is processed when you call the load method. (Processing the input is of course the prerequisite for knowing and thus being able to create the controller instance.)

Related

JavaFX: Make Chips Editable in JFXChipView

I want to ask if it is possible to make a chip in JFXChipView editable once it has been set.
You can create your own JFXChip and implement a behavior to enable editing. First, you need to have an editable label. I looked up online and I found this post: JavaFX custom control - editable label. Then, you can extend JFXChip to use that EditableLabel:
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXChip;
import com.jfoenix.controls.JFXChipView;
import com.jfoenix.svg.SVGGlyph;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
import javafx.scene.layout.HBox;
public class EditableChip<T> extends JFXChip<Property<T>> {
protected final HBox root;
public EditableChip(JFXChipView<Property<T>> view, Property<T> item) {
super(view, item);
JFXButton closeButton = new JFXButton(null, new SVGGlyph());
closeButton.getStyleClass().add("close-button");
closeButton.setOnAction(event -> {
view.getChips().remove(item);
event.consume();
});
// Create the label with an initial value from the item
String initialValue = view.getConverter().toString(item);
EditableLabel label = new EditableLabel(initialValue);
label.setMaxWidth(100);
// Bind the item to the text in the label
item.bind(Bindings.createObjectBinding(() -> view.getConverter().fromString(label.getText()).getValue(), label.textProperty()));
root = new HBox(label, closeButton);
getChildren().setAll(root);
}
}
Note: I am using Property<T> instead of using the desired class T because JFXChipView stores the item the first time you add it. And in that case, you're going to get the values as you entered them the first time when calling JFXChipView#getChips().
Sample application:
import com.jfoenix.controls.JFXChipView;
import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class EditableChipViewApp extends Application {
#Override
public void start(Stage primaryStage) {
JFXChipView<Property<String>> chipView = new JFXChipView<>();
chipView.setChipFactory(EditableChip::new);
chipView.setConverter(new StringConverter<Property<String>>() {
#Override
public String toString(Property<String> object) {
return object == null ? null : object.getValue();
}
#Override
public Property<String> fromString(String string) {
return new SimpleStringProperty(string);
}
});
VBox container = new VBox(chipView);
Scene scene = new Scene(container, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Result:
This is how you get the actual values of the chips:
List<String> chipsValues = chipView.getChips().stream().map(Property::getValue).collect(Collectors.toList());

Missing GUI elements when adding from different files

I am trying to add GUIs, created from individual files and add them into my main code.
While it seems to be working, kind of, however, it is missing some elements. For example, in my GridPane, there are a label and a text, both of which are missing. Likewise, for my treeview, there is a treeitem within, however, that is missing as well.
What I am trying to attempt is to reduce the amount of code in the main field and as well as to call relevant events between the Guis, eg. if I select something in the TreeView, that selected TreeItem information will be populated in the GridPane.
Client.java
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Client extends Application
{
private treeviewGui tvGui;
private gridpaneGui inputFieldsGui;
public void init()
{
tvGui = new treeviewGui();
inputFieldsGui = new gridpaneGui();
}
#Override
public void start(Stage topView)
{
topView.setTitle("Test Application");
HBox mainLayout = new HBox(10);
mainLayout.getChildren().addAll(tvGui, inputFieldsGui);
Scene scene = new Scene(mainLayout);
topView.centerOnScreen();
topView.setScene(scene);
topView.show();
}
public static void main(String[] argv)
{
launch(argv);
}
}
treeviewGui.java
import javafx.scene.control.*;
public class treeviewGui extends TreeView
{
private TreeView treeview;
public treeviewGui()
{
treeview = new TreeView();
preload();
}
private void preload()
{
TreeItem<String> newTI = new TreeItem<>("blah");
treeview.setRoot(newTI);
}
}
gridPane.java
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.geometry.Pos;
import javafx.scene.text.Text;
public class gridpaneGui extends GridPane
{
private GridPane gridPane;
public Text fnameTxt;
public gridpaneGui()
{
gridPane = new GridPane();
gridPane.setAlignment(Pos.CENTER);
gridPane.setHgap(5);
gridPane.setVgap(5);
// First Name
Label fnameLbl = new Label("First Name");
fnameTxt = new Text("-");
gridPane.addRow(0, fnameLbl, fnameTxt);
}
public void setFname(String nameStr)
{
fnameTxt.setText(nameStr);
}
}

Communication between Controller and Main Class in JavaFX

I have just started with JavaFX. I'm in the process of developing a GUI for my program, and I've used SceneBuilder to design my GUI. I currently have a main class that uses an FXMLLoader to load an .fxml file and displays it. This FXML file has another class called "Controller" as controller. As I said, I haven't had that much to do with JavaFX, so it's possible that I'm just doing it wrong.
Now I have 2 problems: First, I want to communicate between the two classes (code follows). Second, I would like to know how to integrate the code from the main class of the GUI into my already existing main class, I already tried it, but I always got only error messages (stacktrace and code follows).
isi_ko
My GUI Main Class
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 {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Parent normalParent = FXMLLoader.load(getClass().getResource("normalLayout.fxml"));
primaryStage.setTitle("Test");
primaryStage.setScene(new Scene(normalParent, primaryStage.getWidth(), primaryStage.getHeight()));
primaryStage.show();
}
}
My GUI Controller Class
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Stage;
public class Controller implements EventHandler<ActionEvent> {
#FXML Label activePercent;
#FXML Label activeLabel;
#FXML Label activeNumber;
#FXML Label lastLable;
#FXML Label lastNumber;
#FXML Label nextLabel;
#FXML Label nextNumber;
#FXML Label faderValue1;
#FXML Label faderValue2;
#FXML Label faderValue3;
#FXML Label faderValue4;
#FXML Label faderValue5;
#FXML
public void test() {
}
#Override
public void handle(ActionEvent event) {
getFaderValueLable(1).setText("1");
getFaderValueLable(2).setText("2");
getFaderValueLable(3).setText("3");
getFaderValueLable(4).setText("4");
getFaderValueLable(5).setText("5");
}
public Label getFaderValueLable(int i){
switch (i){
case 1:
return faderValue1;
case 2:
return faderValue2;
case 3:
return faderValue3;
case 4:
return faderValue4;
case 5:
return faderValue5;
default:
return null;
}
}
}
My normal Main Class (without GUI Code)
import com.fazecast.jSerialComm.SerialPort;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
public class Main{
Fader[] faders;
final int baudRate = 1000000; //The Speed that is Used to Communicate with the Arduino
private int physicalFaderAmount; //Amount of Physical Faders, counted from 1
private int faderPages; //Amount of Faderpages, counted from 1
private int activeFaderpage; //The Currently active "physical" Faderpage
private ArduinoController arduinoController; //The Class that communicates with the Arduino
private EosOscController oscController; //The Class that Communicates with Eos
Main(String[] args) {
//Todo Redo this part when UI done
//at the Moment here are just some default values
activeFaderpage = 0;
physicalFaderAmount = 5;
faderPages = 4;
faders = new Fader[physicalFaderAmount];
for (int i = 0; i < physicalFaderAmount; i++){
faders[i] = new Fader(faderPages, i);
}
oscController = new EosOscController(8001, 8000, "192.168.178.133", this);
arduinoController = new ArduinoController(SerialPort.getCommPort("COM4"), this);
}
public static void main(String[] args) {
Main main = new Main(args);
}
// This Class is for Storing Information about a particular physical Fader
public class Fader {
public int[][] values;
// The last 3 Falues the Fader has on the Different Faderpages.
// The First Koordinate is for the Page, the second coordinate is for which of the last 3 Values you want to use.
// The First Value of Every page is the Current value of the Fader
// The Others are for detecting inconsistent Potivalues
public int faderID; //The number of the Physical Fader, starting from 0.
// Uses local values, not OSC
// Sets the Value of the Fader
// Can send the new Value directly to the Arduino or Eos
public void setValue(int physicalPage, int value, boolean sendToEos, boolean sendToArduino) {
if (value != this.values[physicalPage][2] || (Math.max(values[physicalPage][1], value) - Math.min(values[physicalPage][1], value)) > 1) {
values[physicalPage][0] = value;
//Todo: Test this
if (sendToEos) {
oscController.setFaderIntensity(physicalPage, faderID, value / 100.0D, true);
}
//Todo Test this
if (sendToArduino) {
arduinoController.sendMessage(new ArduinoController.MessageToArduino(faderID, value), true);
}
}
values[physicalPage][2] = values[physicalPage][1];
values[physicalPage][1] = value;
}
Fader(int pages, int faderID) {
values = new int[pages][3];
this.faderID = faderID;
}
}
}
My normal Main Class (with GUI Code)
import com.fazecast.jSerialComm.SerialPort;
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{
Fader[] faders;
final int baudRate = 1000000; //The Speed that is Used to Communicate with the Arduino
private int physicalFaderAmount; //Amount of Physical Faders, counted from 1
private int faderPages; //Amount of Faderpages, counted from 1
private int activeFaderpage; //The Currently active "physical" Faderpage
private ArduinoController arduinoController; //The Class that communicates with the Arduino
private EosOscController oscController; //The Class that Communicates with Eos
Main() {
//Todo Redo this part when UI done
//at the Moment here are just some default values
activeFaderpage = 0;
physicalFaderAmount = 5;
faderPages = 4;
faders = new Fader[physicalFaderAmount];
for (int i = 0; i < physicalFaderAmount; i++){
faders[i] = new Fader(faderPages, i);
}
oscController = new EosOscController(8001, 8000, "192.168.178.133", this);
arduinoController = new ArduinoController(SerialPort.getCommPort("COM4"), this); // For PC
// arduinoController = new ArduinoController(SerialPort.getCommPort("ttyACM0"), this); //For Raspberry Pi
}
public static void main(String[] args) {
launch();
}
#Override
public void start(Stage primaryStage) throws Exception {
Parent normalParent = FXMLLoader.load(getClass().getResource("normalLayout.fxml"));
primaryStage.setTitle("Test");
primaryStage.setScene(new Scene(normalParent, primaryStage.getWidth(), primaryStage.getHeight()));
primaryStage.show();
}
// This Class is for Storing Information about a particular physical Fader
public class Fader {
public int[][] values;
// The last 3 Falues the Fader has on the Different Faderpages.
// The First Koordinate is for the Page, the second coordinate is for which of the last 3 Values you want to use.
// The First Value of Every page is the Current value of the Fader
// The Others are for detecting inconsistant Potivalues
public int faderID; //The number of the Physical Fader, starting from 0.
// Uses local values, not OSC
// Sets the Value of the Fader
// Can send the new Value directly to the Arduino or Eos
public void setValue(int physicalPage, int value, boolean sendToEos, boolean sendToArduino) {
if (value != this.values[physicalPage][2] || (Math.max(values[physicalPage][1], value) - Math.min(values[physicalPage][1], value)) > 1) {
values[physicalPage][0] = value;
//Todo: Test this
if (sendToEos) {
oscController.setFaderIntensity(physicalPage, faderID, value / 100.0D, true);
}
//Todo Test this
if (sendToArduino) {
arduinoController.sendMessage(new ArduinoController.MessageToArduino(faderID, value), true);
}
}
values[physicalPage][2] = values[physicalPage][1];
values[physicalPage][1] = value;
}
Fader(int pages, int faderID) {
values = new int[pages][3];
this.faderID = faderID;
}
}
}
Stacktrace for Main Class with GUI Code
Exception in Application constructor
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Unable to construct Application instance: class Main
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:907)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$1(LauncherImpl.java:182)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NoSuchMethodException: Main.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getConstructor(Class.java:1825)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$7(LauncherImpl.java:818)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$7(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$5(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$6(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$3(WinApplication.java:177)
... 1 more
Exception running application Main
Process finished with exit code 1

Switch fxml pages without creating new controller

I am dealing with JavaFX, as you can see in the picture below, I have a menu (A,B,C,D,E) on the left, all I want to do is that when I click on the menu item, the content of the page change (the red form).
I implemented my code, it works fine visually, but at each time it creates a new controller for each new form. And when it does so I loose the data of my main frame. Here is my Code to explain better.
MainFrame.fxml
<ScrollPane xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller.Inhumer.MainController" >
<BorderPane fx:id="myContent" >
<center >
<fx:include fx:id="demandeur" source="Demandeur.fxml" />
</center>
<left>
<fx:include fx:id="menu" source="SideBar_Inhumer.fxml" />
</left>
</BorderPane>
</ScrollPane>
MainController.java
package Controller.Inhumer;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.layout.BorderPane;
import java.io.IOException;
import java.util.HashMap;
public class MainController {
HashMap<String, Parent> menuItems;
#FXML
public BorderPane myContent ;
#FXML
DemandeurController demandeurController;
#FXML
MenuController menuController;
#FXML public void initialize() throws IOException {
/*load the content of my forms */
Parent rootDemandeur = new FXMLLoader(getClass().getResource("../../View/Inhumer/Demandeur.fxml")).load();
Parent rootDefunt = new FXMLLoader(getClass().getResource("../../View/Inhumer/Defunt.fxml")).load();
Parent rootEmplacement = new FXMLLoader(getClass().getResource("../../View/Inhumer/Emplacement.fxml")).load();
Parent rootPrestataire = new FXMLLoader(getClass().getResource("../../View/Inhumer/Prestataire.fxml")).load();
Parent rootOperation = new FXMLLoader(getClass().getResource("../../View/Inhumer/Operation.fxml")).load();
menuItems = new HashMap<>();
menuItems.put("demandeur",rootDemandeur); //A
menuItems.put("defunt",rootDefunt); //B
menuItems.put("emplacement",rootEmplacement); //C
menuItems.put("prestataire",rootPrestataire); //D
menuItems.put("operation",rootOperation); //E
System.out.println("Application started");
demandeurController.init(this);
menuController.init(this);
}
}
MenuController.java
package Controller.Inhumer;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.layout.BorderPane;
import java.io.IOException;
public class MenuController {
private MainController main;
/*methods called onClick on each item of my menu*/
#FXML
public void goToDemandeur() throws IOException {
main.myContent.setCenter(main.menuItems.get("demandeur"));
}
#FXML
public void goToDefunt() throws IOException {
main.myContent.setCenter(main.menuItems.get("defunt"));
}
#FXML
public void goToEmplacement() throws IOException {
main.myContent.setCenter(main.menuItems.get("emplacement"));
}
#FXML
public void goToPrestataire() throws IOException {
main.myContent.setCenter(main.menuItems.get("prestataire"));
}
#FXML
public void goToOperation() throws IOException {
main.myContent.setCenter(main.menuItems.get("operation"));
}
public void init(MainController mainController) {
main = mainController;
}
}
DemandeurController.java (the controller of the red form)
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class DemandeurController implements Initializable {
public MainController mainController;
/*Onclick action on the button called "suivant"*/
#FXML
public void next() throws IOException {
System.out.println(mainController);
/*print null, because when loading this form mainController will get
null value, and I want to always get the value of mainController
so I can access to its content from this controller*/
}
public void init(MainController mainController) {
this.mainController = mainController;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
I hope that I clarified well the problem, any help? I am blocked here for two days :v

JavaFX App: Switching between Panes won't load set content

I have desktop app with side menu bar. Main window is BorderPane with InsertLeft containing VBox. I set Hbox buttons and their behaviour then I add them one by one to the VBox. InsertCenter has just Pane with alot of elements.
I've created 3 fxml files for each GUI layout.
sample.fxml - BorderPane: InsertLeft->Menu(VBox), InsertCenter->Empty Pane
tab1_content.fxml - Pane filled with ProgressBar, Labels and Buttons
tab2_content.fxml - Not yet implemented (Empty Pane)
Each of these fxml files has their controllers.
I would like to switch content of borderPane.center() inside sample.fxml on menu button click.
I've managed to fix some issues, but main problem is with loading data into .fxml views.
As I run my App it works perfectly, each fxml file has his FXMLLoader which will load content into borderPane right inside main Controller.
Problem occurs while I click on Buttons. It will switch panes, but actual content will reset to default state and Main.class initialization is completely ignored. Button listeners and label values are not initialized. It's just empty fxml layout. Every variable inside Main.class, what I want to access from Tab1Controller is returning NullPointerException - even methods.
Each controller extends AbstractController, which contains Main.class instance which is initialized inside start() method. So i should be able to have access to every Main.class method/variable at any time.
Issue Gif:
Some code samples:
My Main.class start() method:
public Controller myController;
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/sample.fxml"));
myController = new Controller();
myController.setMainApp(this);
loader.setController(myController);
Parent root = loader.load();
primaryStage.setTitle("Simple App");
primaryStage.setScene(new Scene(root));
primaryStage.show();
<other stuff>
}
public void setDefaultViewProperties(){
currentScanningFileProperty = new SimpleStringProperty();
myController.tab1Controller.actualPath.textProperty().bind(currentScanningFileProperty); //NullPointerException while called from Controller
fileCounterProperty = new SimpleLongProperty();
myController.tab1Controller.numOfScanned.textProperty().bind(fileCounterProperty.asString());
maliciousFilesCounterProperty = new SimpleIntegerProperty();
myController.tab1Controller.numOfMaliciousFiles.textProperty().bind(maliciousFilesCounterProperty.asString());
myController.tab1Controller.fileChoiceBtn.setOnMouseClicked(event -> chooseFile());
myController.tab1Controller.runScanBtn.setOnMouseClicked(event -> new Thread(() -> {
try {
resetValues();
startFileWalking(chosenFile);
} catch (IOException e) {
e.printStackTrace();
}
}).start());
}
MainController:
package sample;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller extends AbstractController implements Initializable{
public HBox sideMenu;
public VBox mainMenu;
public BorderPane borderPane;
public Boolean isButton1Pressed = false;
public Boolean isButton2Pressed = false;
public static final String TAB_1 = "TAB-1";
public static final String TAB_2 = "TAB-2";
public Button malwareButton;
public Button webShieldButton;
public Tab1Controller tab1Controller;
public Tab2Controller tab2Controller;
#Override
public void initialize(URL location, ResourceBundle resources) {
createMenuButtons();
setSideMenu();
setMenuButtonsListeners();
}
private void setSideMenu(){
mainMenu.getChildren().add(item(malwareButton));
mainMenu.getChildren().add(item(webShieldButton));
mainMenu.setStyle("-fx-background-color:#004D40");
}
private HBox item(Button menuButton){
menuButton.setPrefSize(200, 50);
menuButton.setStyle("-fx-background-color: transparent;");
menuButton.setTextFill(Color.web("#E0F2F1"));
menuButton.setPadding(Insets.EMPTY);
sideMenu = new HBox(menuButton);
return sideMenu;
}
public void setMenuButtonsListeners(){
malwareButton.setOnMousePressed(event -> {
setButtonStylePressed(malwareButton);
setButtonStyleUnpressed(webShieldButton);
isButton1Pressed = true;
isButton2Pressed = false;
loadTab1Content();
main.setDefaultViewProperties();
});
webShieldButton.setOnMousePressed(event -> {
setButtonStylePressed(webShieldButton);
setButtonStyleUnpressed(malwareButton);
isButton1Pressed = false;
isButton2Pressed = true;
loadTab2Content();
});
malwareButton.setOnMouseExited(event -> {
if(!isButton1Pressed){
setButtonStyleUnpressed(malwareButton);
}
});
webShieldButton.setOnMouseExited(event -> {
if(!isButton2Pressed){
setButtonStyleUnpressed(webShieldButton);
}
});
malwareButton.setOnMouseEntered(event -> setButtonStylePressed(malwareButton));
webShieldButton.setOnMouseEntered(event -> setButtonStylePressed(webShieldButton));
}
public void setButtonStylePressed(Button btn){
btn.setStyle("-fx-background-color: #E0F2F1");
btn.setTextFill(Color.web("#004D40"));
}
public void setButtonStyleUnpressed(Button btn){
btn.setStyle("-fx-background-color: transparent");
btn.setTextFill(Color.web("#E0F2F1"));
}
private void loadTab1Content(){
FXMLLoader tab1loader = new FXMLLoader();
tab1loader.setLocation(getClass().getResource("/tab_1_content.fxml"));
try {
if (tab1Controller == null){
tab1Controller = new Tab1Controller();
}
tab1loader.setController(tab1Controller);
borderPane.setCenter(tab1loader.load());
} catch (IOException e) {
e.printStackTrace();
}
}
private void loadTab2Content(){
FXMLLoader tab2loader = new FXMLLoader();
tab2loader.setLocation(getClass().getResource("/tab_2_content.fxml"));
try {
if (tab2Controller == null){
tab2Controller = new Tab2Controller();
}
tab2loader.setController(tab2Controller);
borderPane.setCenter(tab2loader.load());
} catch (IOException e) {
e.printStackTrace();
}
}
private void createMenuButtons(){
malwareButton = new Button();
malwareButton.setText(TAB_1);
webShieldButton = new Button();
webShieldButton.setText(TAB_2);
}
}
Tab1Controller:
package sample;
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.ProgressBar;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.ResourceBundle;
/**
* Created by admin on 5. 5. 2018.
*/
public class Tab1Controller extends AbstractController implements Initializable {
public ProgressBar progressBar;
public Button runScanBtn;
public Button fileChoiceBtn;
public Label chosenPath;
public Label actualPath;
public Label numOfMaliciousFiles;
public Label hashValue;
public Label scanFinishedMsg;
public Label numOfScanned;
public Button showFoundMalwareButton;
#Override
public void initialize(URL location, ResourceBundle resources) {
runScanBtn.setDisable(true);
scanFinishedMsg.setVisible(false);
showFoundMalwareButton.setVisible(false);
showFoundMalwareButton.setOnAction(event -> showPopupWindow());
}
Update#1 - Updating fxml values through Main.class after button click
I've finally managed to run app without exception. I had to create next Controller for pane fxml layout itself called Tab1Controller. When I initialized Controller, it instantly initialized Tab1Controller inside. So when I want to change Center BorderPane label i had to call myController.tab1Controller.tabLabel.setText()
I don't know if it's good approach to this problem.
But now I'm back to my old problem. When I click on TAB-1 it will load content, but values are not initialized to default state.
For example I have couple of labels updated in real time. I binded some SimpleProperties into it with default values. It worked before, but as I have three controllers it will load data for a first time, but when I click TAB-1 button it will load just fxml content, but it will not set those labels.
So i made public method inside Main.class which I will call everytime I switch to TAB-1 from Controller.
public void setDefaultViewProperties(){
myController.tab1Controller.actualPath.textProperty().bind(currentScanningFileProperty);
myController.tab1Controller.numOfScanned.textProperty().bind(fileCounterProperty.asString());
myController.tab1Controller.numOfMaliciousFiles.textProperty().bind(maliciousFilesCounterProperty.asString());
}
But now everytime I click on TAB-1 I've got
java.lang.NullPointerException: Cannot bind to null
You can make two pane and switch between them using setVisible() method
example:
void btn1Clicked() {
pane1.setVisible(true);
pane2.setVisible(false);
}
void btn2Clicked() {
pane1.setVisible(false);
pane2.setVisible(true);
}
You could use a TabPane to achieve this behaviour:
https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TabPane.html
Solved. I'm not sure how, but setDefaultViewProperties() are not throwing NullPointerException at the moment. I did not change anything inside the code:
malwareButton.setOnMousePressed(event -> {
setButtonStylePressed(malwareButton);
setButtonStyleUnpressed(webShieldButton);
isButton1Pressed = true;
isButton2Pressed = false;
loadTab1Content();
main.setDefaultViewProperties();
});

Categories