How to run a code block after the view loads - javafx

When using an fxml file and controller, it is possible to have an initialize method, which runs before the view loads. Similarly, is there a way to run a block of code after the view loads?
To be more specific, I have a Rectangle in the fxml file. I want to call the Rectangle's setFill() , method, from the controller, after the view loads and pass a LinearGradient instance. LinearGradient has properties without setters and doesn't have a parameterless constructor, so I don't think the fill property can be assigned in the fxml file.
I tried putting this code in the constructor of the controller but that results in an error. If possible, I would also like to know what procedure takes place with regards to the controller class when FXMLLoader loads an fxml file (when is a constructor called).
Please take a look at this sample code if necessary:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.shape.Rectangle?>
<Pane fx:controller="sample.RectController" xmlns:fx="http://javafx.com/fxml">
<Rectangle fx:id="rect" height="200.0" width="200.0" />
</Pane>
package sample;
import javafx.fxml.FXML;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Rectangle;
public class RectController{
#FXML Rectangle rect;
{
//code i would like to run but currently results in an error
rect.setFill(new LinearGradient(
0, 0.5, 1, 0.5, true, CycleMethod.NO_CYCLE,
new Stop(0, Color.BLUE),
new Stop(1, Color.RED)
));
}
}

You want your controller to implement a no-arg initialize() method annotated with #FXML (The old way was to implement javafx.fxml.Initializable but as you can see from the docs for that method, you don't need the interface anymore.)
You can't do it in the controller's constructor because the fields haven't been injected yet by the FXMLLoader.

Related

Get Controller instance from Node

There is an possibility to get Controller instance from Node ?? for example AnchorPane on Tab ?
I have some AnchorPanes where I load different Controllers and I would like to verify which controller is already loaded
Nodes do not contain any information about a controller used with the fxml file used to create it by default, since fxml is just one way of creating a scene. However you could attach information like this to the userData/properties in the fxml:
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" fx:id="AnchorPane" prefHeight="400.0" prefWidth="600.0" onMouseClicked="#click" fx:controller="fxml.FXMLController">
<!-- store controller as userData property -->
<userData>
<fx:reference source="controller" />
</userData>
<properties>
<!-- store controller at key "foo" in properties map -->
<foo><fx:reference source="controller" /></foo>
</properties>
</AnchorPane>
If you do this, you can lookup the controller at closest ancestor of a node where you added this kind of information using
public static Object getController(Node node) {
Object controller = null;
do {
controller = node.getProperties().get("foo");
node = node.getParent();
} while (controller == null && node != null);
return controller;
}
to retrieve the info from the properties map or using
public static Object getController(Node node) {
Object controller = null;
do {
controller = node.getUserData();
node = node.getParent();
} while (controller == null && node != null);
return controller;
}
to retrieve the info from the userData property.
You should probably use just one way of adding the info though, not both. Also it would be better to replace foo as key...
It's an old question, but if you have a main window, where you include other fxml files like this:
<AnchorPane prefHeight="900.0" prefWidth="1600.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="MainController">
<!-- <HBox></HBox>, some elements here, your normal usual FXML-->
<fx:include fx:id="someAnchorPane" source="SomeAnchorPane.fxml"/>
</AnchorPane>
and your SomeAnchorPane.fxml has fx:controller property set to SomeOtherController, then you can add a controller field in your MainController like this:
#FXML private SomeOtherController someAnchorPaneController;
And it will inject appropriate controller in this field automatically.
The key thing here is that your field has to be named "fx:id+Controller" for it to work.

Javafx FXML loader getcontroller returns null

In my program i have a tabPane with each tab having in own FXML file and controller .I have loaded the FXML file for tab 2 (Schedular).I`m trying to call a function in my child controller (Scheduler) from my parent controller(FXML Document) . When i load the controller using FXML Loader it returns a null. How can i solve this .
Here is my main code:
Main FXMLDocument :
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" fx:id="AnchorPane" prefHeight="600" prefWidth="800" xmlns="http://javafx.com/javafx/8.0.65" fx:controller="showprojavafxml.FXMLDocumentController">
<children>
The FXMLLoader only instantiates the controller when it loads the fxml file (this has to be the case, since the controller class is specified in the fxml file...). Since you never call load() on the loader, the controller is never created.
It looks like you are referencing the FXML file twice through two different mechanisms: once in FXMLDocumentController.initialize(), where you create a FXMLLoader whose location is set to the fxml file, and once in the main FXML document itself, via a <fx:include>. The <fx:include> is causing the UI defined in Scheduler.fxml to be displayed; the FXMLLoader you create in the initialize() method is not (because you never call load() and display the result).
To reference a controller for an included fxml, use the "Nested controller" technique.
First, add an fx:id to your fx:include:
<Tab fx:id="tab2" text="Scheduler" >
<fx:include fx:id="scheduler" source="Scheduler.fxml" />
</Tab>
Now you can inject the controller into a field whose name is the fx:id with the text "Controller" appended:
public class FXMLDocumentController {
#FXML
private SchedulerController schedulerController ;
#Override
public void initialize(URL url, ResourceBundle rb) {
schedulerController.refreshList();
}
}

How to access UI components from SceneBuilder in JavaFX

(DUPLICATE & SOLVED - see answer below)
I'm doing my first steps in JavaFX and it seems quite hard to use the "SceneBuilder". I'm used to Android and the QtCreator. It looks to me that there is accessing the UI components much easier.
Something like findViewById(R.id.btnPushMe); <- Android Code
Actually I got an solution but it is quite uncomfortable to use. This looks as this:
FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("../fmxl/main.fxml"));
AnchorPane pane = loader.load();
System.out.println("panechilds:" + pane.getChildren().size());
BorderPane border = (BorderPane) pane.getChildren().get(0);
System.out.println("borderchilds:" + border.getChildren().size());
the xml..
<AnchorPane fx:id="mAnchor" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.progui.MainController">
<children>
<BorderPane layoutX="-1.0" prefHeight="600.0" prefWidth="800.0">
<top>
...
Thanks in advance
Martin
Edit:
This is a duplicate question (but I will not delete it, because I took some time to find the answer - maybe because JavaFX wasn't asked as much as Android questions were..)
AnchorPane anchor = (AnchorPane) scene.lookup("#mAnchor");
found here: How to find an element with an ID in JavaFX?
You should use a controller class and access the UI elements there.
Basically you do:
<AnchorPane fx:id="mAnchor" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.progui.MainController">
<children>
<BorderPane fx:id="border" layoutX="-1.0" prefHeight="600.0" prefWidth="800.0">
<top>
...
And then you can access the fx:id-attributed elements in the controller with
package app.progui ;
// ...
public class MainController {
#FXML
private BorderPane border ;
public void initialize() {
border.setStyle("-fx-background-color: antiquewhite;");
// ...
}
// ...
}
The field names in the controller class must match the fx:id values in the FXML file.
It is possible to access the fx:id-attributed elements in the class that invoked the FXMLLoader, but if you need to do this it is usually a sign that your overall design is wrong. You can do:
FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("../fmxl/main.fxml"));
AnchorPane pane = loader.load();
Map<String, Object> fxmlNamespace = loader.getNamespace();
BorderPane border = (BorderPane) fxmlNamespace.get("border");
assuming the fx:id defined in the FXML snipped above.
When you design for FXML, you typically design three things: the application logic, the GUI controller logic, and the FXML.
References to the UI controls you wish to access are injected by the FXML loader into your controller class during its loading and initialization so that you do not need to use a FindById() method.
A controller class looks similar to this:
class DCServRecEditor extends DialogController {
#FXML // ResourceBundle that was given to the FXMLLoader
private ResourceBundle resources;
#FXML // URL location of the FXML file that was given to the FXMLLoader
private URL location;
#FXML // fx:id="ancMatchSelector"
private AnchorPane ancMatchSelector; // Value injected by FXMLLoader
#FXML // fx:id="ancServEditor"
private AnchorPane ancServEditor; // Value injected by FXMLLoader
#FXML // fx:id="ancServRecEditor"
private AnchorPane ancServRecEditor; // Value injected by FXMLLoader
:
:
The FXML loading facility automatically injects references into instance fields that are annotated with the #FXML tag. To manipulate a UI control, just access its methods using the appropriate reference.
Separating the UI control logic from your application logic is highly desirable, and effects a "separation of concerns". When you get used to designing FXML UI's this way, you'll enjoy it.

AddChild with Dynamic DispalyObject in Flex

I am pretty sure that this is totally what i need, however I can not get it to work for some reason. What i would like to do is call an arbitrary component that extends Canvas. Since there may be a variety of components named TestCanvasA, TestCanvasC, TestCanvasC which i won't know till run-time I figured this would be the way to go about it. Here is what i have.
<mx:Script>
<![CDATA[
import component.TestCanvas;
import mx.containers.Canvas;
import flash.display.Sprite;
import flash.utils.getDefinitionByName;
private function init(evt:Event):void{
var Type:String="TestCanvas";
var controlClass:Class = getDefinitionByName(Type) as Class;
this.addChild(new controlClass() as Canvas);
}
]]>
</mx:Script>
Any ideas would be awesome!
Give it the fully qualified class name:
var type:String="component.TestCanvas";
var controlClass:Class = getDefinitionByName(Type) as Class;
Also a mere import statement need not include the definition of the class in the compiled SWF unless there are references to the class inside the application. Just declare (need not initialize) a variable of that type somewhere in the SWF to make sure that the definition is indeed included.
var dummy:TestCanvas;

Possible to use Flex Framework/Components without using MXML?

Is it possible to use the Flex Framework and Components, without using MXML? I know ActionScript pretty decently, and don't feel like messing around with some new XML language just to get some simple UI in there. Can anyone provide an example consisting of an .as file which can be compiled (ideally via FlashDevelop, though just telling how to do it with the Flex SDK is ok too) and uses the Flex Framework? For example, just showing a Flex button that pops open an Alert would be perfect.
If it's not possible, can someone provide a minimal MXML file which will bootstrap a custom AS class which then has access to the Flex SDK?
I did a simple bootstrap similar to Borek (see below). I would love to get rid of the mxml file, but if I don't have it, I don't get any of the standard themes that come with Flex (haloclassic.swc, etc). Does anybody know how to do what Theo suggests and still have the standard themes applied?
Here's my simplified bootstrapping method:
main.mxml
<?xml version="1.0" encoding="utf-8"?>
<custom:ApplicationClass xmlns:custom="components.*"/>
ApplicationClass.as
package components {
import mx.core.Application;
import mx.events.FlexEvent;
import flash.events.MouseEvent;
import mx.controls.Alert;
import mx.controls.Button;
public class ApplicationClass extends Application {
public function ApplicationClass () {
addEventListener (FlexEvent.CREATION_COMPLETE, handleComplete);
}
private function handleComplete( event : FlexEvent ) : void {
var button : Button = new Button();
button.label = "My favorite button";
button.styleName="halo"
button.addEventListener(MouseEvent.CLICK, handleClick);
addChild( button );
}
private function handleClick(e:MouseEvent):void {
Alert.show("You clicked on the button!", "Clickity");
}
}
}
Here are the necessary updates to use it with Flex 4:
main.mxml
<?xml version="1.0" encoding="utf-8"?>
<local:MyApplication xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:local="components.*" />
MyApplication.as
package components {
import flash.events.MouseEvent;
import mx.controls.Alert;
import mx.events.FlexEvent;
import spark.components.Application;
import spark.components.Button;
public class MyApplication extends Application {
public function MyApplication() {
addEventListener(FlexEvent.CREATION_COMPLETE, creationHandler);
}
private function creationHandler(e:FlexEvent):void {
var button : Button = new Button();
button.label = "My favorite button";
button.styleName="halo"
button.addEventListener(MouseEvent.CLICK, handleClick);
addElement( button );
}
private function handleClick(e:MouseEvent):void {
Alert.show("You clicked it!", "Clickity!");
}
}
}
This is a very simple app that does only the basic bootstrapping in MXML. This is the MXML:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="onCreationComplete()">
<mx:Script source="Script.as" />
</mx:Application>
This is the Script.as:
import mx.controls.Button;
import flash.events.MouseEvent;
import mx.controls.Alert;
import mx.core.Application;
private function onCreationComplete() : void {
var button : Button = new Button();
button.label = "Click me";
button.addEventListener(MouseEvent.CLICK, function(e : MouseEvent) : void {
Alert.show("Clicked");
});
Application.application.addChild(button);
}
NOTE: The below answer will not actually work unless you initialize the Flex library first. There is a lot of code involved to do that. See the comments below, or other answers for more details.
The main class doesn't even have to be in MXML, just create a class that inherits from mx.core.Application (which is what an MXML class with a <mx:Application> root node is compiled as anyway):
package {
import mx.core.Application;
public class MyFancyApplication extends Application {
// do whatever you want here
}
}
Also, any ActionScript code compiled with the mxmlc compiler -- or even the Flash CS3 authoring tool -- can use the Flex classes, it's just a matter of making them available in the classpath (refering to the framework SWC when using mxmlc or pointing to a folder containing the source when using either). Unless the document class inherits from mx.core.Application you might run in to some trouble, though, since some things in the framework assume that this is the case.
Yes, you just need to include the flex swc in your classpath. You can find flex.swc in the flex sdk in frameoworks/lib/flex.swc
edit: One more thing: if you're using Flex Builder you can simply create a new ActionScript project, which will essentially do the same as above.

Resources