I'm creating custom components using FXML. The custom components are designed in a hierarchical fashion.
When I design a custom component B that uses another custom component A, a classpath problem dialog pops up in scenebuilder and I simply fix this by setting the appropriate classpath.
However when I create three components, say C containing B containing A, and try to open top-level component C in Scenebuilder it fails. It asks me for classpaths which I duly specify. It finds B but does not find A.
The classpath, FXML and the code is correct as the application is able to execute properly. Only Scenebuilder is having problems.
How should one open hierarchical custom component with Scenebuilder?
Any reference to an example with hierarchical component definitions using FXML would be greatly appreciated and get a bounty of 50 points. (only 3 levels needed)
Someone named David did answer your question on the forum.
For legacy purpose I post it here.
There is a problem with the classloader in Scene Builder for custom components.
When you load a FXML file in SceneBuilder: it uses a FXMLLoader with its own classloader.
In order to load custom components which use their own FXMLLoader to load other custom components, it is necessary to make all FXMLLoader use the same classloader.
As David said on the forum, you can achieve that by adding this code in your custom component.
public class CustomC extends VBox {
public CustomC() {
init();
}
private void init() {
FXMLLoader loader = new FXMLLoader();
loader.setRoot(this);
loader.setLocation(this.getClass().getResource("CustomC.fxml"));
// Make sure to load "CustomC.fxml" with the same classloader that
// was used to load CustomC class.
loader.setClassLoader(this.getClass().getClassLoader());
try {
final Node root = (Node)loader.load();
assert root == this;
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
}
If you want to externalize this code in a class, it is important to put this class in the same jar as your custom components: you cannot put it in a external jar (at least for now).
Related
I'm on Mojarra 2.2.13 and my project uses PrimeFaces 6.0.
I'm writing my own JSF UIComponent. It requires a bit of JavaScript located in webapp/resources/js/charts.min.js. When I annotate my component using #ResourceDependency the script is rendered:
#ResourceDependency(name = "js/charts.min.js", target = "head")
But, I don't always need it to be rendered. So I was trying to conditionally add a component resource to the view root from within the encodeBegin(FacesContext context) method:
if (condition) {
UIOutput js = new UIOutput();
js.setRendererType("javax.faces.resource.Script");
js.getAttributes().put("name", "js/charts.min.js");
context.getViewRoot().addComponentResource(context, js, "head");
writer.startElement("div", null);
writer.writeAttribute("class", "myChart", null);
// ... write chart data
writer.endElement("div");
}
This does not render the script (myChart is rendered though). No errors appear in my log. Any ideas what I could check or improve?
I've also tested without PrimeFaces (not sure if its head renderer was causing this), but the result is the same.
So, encodeBegin(FacesContext context) is not the correct location to add resources. You are too late there.
I've moved the code to the constructor of the component and now the script is added. I'm not 100% this is the best location to do so, but I've seen component libraries doing it in the constructor as well. It also works together with PrimeFaces.
See also:
How to programmatically add JS and CSS resources to <h:head>?
I'm building an Android and iOS app using Xamarin Forms.
What I'm simply trying to do is set a background drawable on my Android app for my ListView items. The root view of my ListView items are StackLayout's:
var listView = new ListView
{
ItemsSource = items,
ItemTemplate = new DataTemplate(() =>
{
return new ViewCell
{
View = new StackLayout(...)
};
}
};
I know I can access the native element by using a custom renderer:
public class MyEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (e.OldElement == null) {
var nativeEditText = (EditText)Control;
...
}
}
}
But I'm not sure how this would work for a StackLayout (or any other layout for that matter).
I first extended StackLayout:
public class ListViewItem : StackLayout
{
}
And I read somewhere that layouts use the VisualElementRenderer, so I tried the following:
public class ListViewItemRenderer : VisualElementRenderer<StackLayout>
{
protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
{
base.OnElementChanged(e);
// any way to access the native element?
}
}
But VisualElementRenderer does not seem to give me access to the native element.
So is there any way I can access the native elements of Layout elements? Or maybe there is a different way to simply set a background drawable on layouts within my Android app?
Even though I still don't know how to access the native element of a layout, the VisualElementRenderer has a method for setting the background drawable on Android (which was exactly what I needed). So I ended up with the following:
protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
{
base.OnElementChanged(e);
SetBackgroundDrawable(Resources.GetDrawable(Resource.Drawable.listViewItem));
}
I understand you want to hook into an existing Layout renderer and extending it to access the native element with extra capabilities like background image.
Eventually the support for background-image will be supported just like background-colour is, I imagine, across the Layout controls. It may be worth while waiting for this as I can't see why they wouldn't implement these in a later release.
In the mean time you would need something that would work and is quite easy to implement?
Creating the background drawable via inheriting the renderer from a Layout may not be the simplest of solutions therefore, although does have its advantages as you can then re-use easily with the extra functionality across all Layouts for an application.
In your code for ListViewItemRenderer, however, it is inheriting from a Xamarin.Forms control (you specified StackLayout) and have not specified a native, platform dependent, control to be the base for the layout control that would have to match the Xamarin.Forms platform dependent control used.
Each Renderer is tied to a native element. Layout controls will be no different than other custom native control renderers.
For a custom control, you will write a renderer something like the following (note I haven't specified a layout renderer as I haven't had a need to do this yet and am just going from past experience - but similar rules should apply to implementing a renderer for a layout as opposed to a custom control):-
// System.Windows.Controls.Grid in this case is the root native control for a WindowsPhone renderer of MyControl
public class MyControlRenderer : ViewRenderer<MyControlView, System.Windows.Controls.Grid>
There is a simpler approach, however to achieve what you want to do:-
The simpler approach would be instead of inheriting from the Stack Layout control, it would be better to inherit from Grid as the root of the control.
Then you can add an Image control to the Grid and also a Stack Layout for the same Grid Row and Column.
By doing the above you will be able to achieve a background-image across the entire listview item row.
WHAT I HAVE is a standard JavaFX application: Main.java, MainController.java & main.fxml. To add custom component, I created CustomComponentController.java and custom_component_controller.fxml.
PROBLEM is that in CustomComponentController methods I need to reference other methods and standard components from MenuController. I add public static MainController mc; to MainController class body, so that it can be seen from CustomComponentController (MainController.mc.neededMethod()). Then I try to pass everything to it in MainController.initialize() method (mc = this;) - when debugging this breakpoint, I see this full of components instances, but mc remains with null components afterwards.
QUESTION is how to reference the running instance of MainController to use its components and methods in other classes and to crossreference different custom components from each other? How to clean MainController code from event handlers and assistance methods of components by moving it all to component's own class?
I tried the following approaches, but found no way to make them work without errors:
Accessing FXML controller class
How can I access a Controller class in JavaFx 2.0?
JavaFX 2.0 + FXML. Updating scene values from a different Task
JavaFX 2.2 -fx:include - how to access parent controller from child controller
The problem can be solved if you comply the following conditions:
Not only public, but obligatory static MainController mc should be.
Do not forget id in fxml for CustomComponentController: <CustomComponentController fx:id="cc"/>, where cc is the name of the "#FXML imported" CustomComponentController in your MainController class.
Omit parameter fx:controller="main.CustomComponentController" in custom_component_controller.fxml as it results in "Controller value already specified" error (a conflict between main.fxml and custom_component_controller.fxml markup declared controllers).
Put mc = this; in the beginning of MainController's initialize() method. Before using mc in CustomComponentController class, check if it's not null. It can be null when all components, including CustomComponentController, are instantiated at application startup, but there is no mc instance yet. MainController method initialize() where MainController is instantiated is called after components are loaded. Therefore better practice is to use approach in the next paragraph.
In main.fxml create primary component of the same type that CustomComponentController and with the only fx:id parameter. Replace primary component with your CustomComponentController by creating reloadCustomComponents() method and calling it from CustomComponentController's initialize() method. Do it by adding the following to reloadCustomComponents() method:
customComponentAnchorPane.getChildren().remove(customComponent);
customComponent = new customComponent();
customComponentAnchorPane.getChildren().add(customComponent);
Thus all components can be placed outside CustomComponentController with all their methods and reloaded at the startup of the apllication. All component declarations stay in MainController class and can be reached through MainController mc reference. No duplicate creating of components in detail with parameters is needed.
Your problem looks like the classic catalog-crud forms updating, I implemented an interface that I called Updatable with an update method so I could reference any catalog form with any crud form easy after passing Controller Main Class as the UserData Property of the Child Root Component's Form
Hope it Can Solve your problem
If I declare something like
#FXML
private final static double PREF_SPACING = 10d;
or
#FXML
private Insets insets = new Insets(10d);
in the controller class,
is there a way to use their values in Scene Builder?
When I want to change the value, I want to change
it only once, in the controller class.
PRELIMINARY ANSWER
I haven't yet tried all of the techniques below, but it seems to be the way you would do it from reading the documentation. If I get some time, I'll try it out later and update this answer with results (or somebody else can do this and post a new answer or edit this one to create a definitive answer). I just wanted to publish something now to point you in what I believe to be the right direction.
If the below is not what you are looking for, add a few more specifics to your questions to fully describe what you want.
Don't using the #FXML annotation here. #FXML is for injecting values from the markup into the controller, not the other way around.
For your first example which is a constant, let's say your controller class is:
class MyControllerType {
public final static double PREF_SPACING = 10d;
}
Then in your fxml, reference the constant:
<?import MyControllerType?>
...
<VBox>
<spacing><MyControllerType fx:constant="PREF_SPACING"/></spacing>
</VBox>
For your second sample which is not a constant or a part of the SceneGraph, you can use an fx:define element to instantiate an instance of the class. Note that you can't directly instantiate an Insets class from FXML as it has no builder class nor zero length constructor. So what you might be able to do is create another placeholder class for the information and instantiate a reference to that in your FXML (or you can create a Builder that FXML can use to instantiate the Insets).
class InsetsHolder {
private Insets insets = new Insets(10d);
public Insets getInsets();
}
<?import InsetsHolder?>
<fx:define>
<InsetsHolder fx:id="i"/>
</fx:define>
<VBox>
<Button text="Click Me!" VBox.margin="$i.insets"/>
</VBox>
SceneBuilder should be able to read fxml files which use the fx:define and fx:constant notation, as well as (possibly) make use of the reference expression $i.insets. SceneBuilder might not have any UI to allow you to edit the values from within the SceneBuilder application, so you will probably need to hand edit the fxml file portions related to the fx:define and fx:constant elements if you wish to make use of these structures.
There is an executable example of using an fx:define element in this mailing list post on designing resolution independent units in FXML.
In general, I think I'd be a bit cautious of maintaining these kind of dependencies between fxml and java code. It may be more prudent to do more of this kind of stuff in plain Java code within the context of the controller initialize method as scottb suggests.
The #FXML annotation enables the JavaFX objects whose names you defined (fx:id) to have their references reflectively injected into nonpublic fields in the controller object as the scene graph is loaded from the fxml markup.
To the best of my knowledge, this is a one way operation. There is no provision for having named static class variables in the controller object injected into the scene graph during loading.
You can accomplish something very similar to what you are requesting by defining the values that you want set as class variables in your controller object's class, and then setting the appropriate object properties programmatically (rather than in markup) in the initialize() method of your controller object.
The initialize() method is called (if it is present) after the loading of the scene graph is complete (so all the GUI objects will have been instantiated) but before control has returned to your application's invoking code.
The JSF component SelectOneRadio layout is very limited so I wrote a custom Renderer for it, and it works great. However, there are times when I want to use the standard SelectOneRadio layout as well. So I decide to make my new component that utilize the custom Renderer I create, but I want this new component to mirror the functionality of SelectOneRadio, and the only different is that it will use my Renderer. Do I need to create both custom tag and custom component to go with my custom renderer in this case? What class should I extends to obtain all functionalities from SelectOneRadio? I would greatly appreciated if you can provided some codes.
EDIT
#BalusC: I like your idea about detecting the value of layout to delegate the correct renderer. So if I have layout="div_layout", then it works great, but if it is pageDirection or lineDirection and nothing show up. What I did is: I create a class that extends MenuRenderer and I override encodeEnd method, so in there I did this
String layout = (String) component.getAttributes().get("layout");
if(layout != null){
if(layout.equals(PAGE_DIRECTION) || layout.equals(LINE_DIRECTION)){
super.encodeEnd(context, component);
return;
} else if (!layout.equals(DIV_LAYOUT)){
//Throw error message
}
}
//Continue with my own renderer code
EDIT2
Above when I said nothing show up, I was wrong. super.encodeEnd(context, component); did render, but instead of render the radio, it render select option tag. So it seems that I delegate to the wrong renderer. I need to use RadioRenderer instead of MenuRenderer.
If it's specific to your own web application, then you could replace just alone the renderer. Easiest is to extend the implementation specific renderer and then depending on the value of one of the standard attributes (layout is the best choice) either delegate to the implementation specific renderer, or do your own custom rendering job.
I case of Mojarra, you'd like to extend com.sun.faces.renderkit.html_basic.RadioRenderer and then register it as follows
<renderkit>
<renderer>
<component-family>javax.faces.SelectOne</component-family>
<renderer-type>javax.faces.Radio</renderer-type>
<renderer-class>com.example.ExtendedRadioRenderer</renderer-class>
</renderer>
</renderkit>
If you wish to be implementation independent, then you'd need to write the entire renderer implementation yourself.
If you wish to have a custom component for it, then you'd need to write it yourself as well.