Best Practices for managing JavaFX Screens
I tried a few approaches for managing "screens" from fxml centrally, but none was satisfactory to me.
I tried implementing a dynamic registry, but searching through the classpath was too complicated for me.
I tried having a public static method to load the scene, but that meant a lot of duplicated code for loading fxml and needlessly implemented methods that all do the same.
My internal code quality metrics tell me that thats not a good solution either.
So are there any libraries that i could look at or well known patterns that i could follow?
44 Replies
⌛
This post has been reserved for your question.
Hey @NeoCortex97! Please useTIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here./close
or theClose Post
button above when your problem is solved. Please remember to follow the help guidelines. This post will be automatically marked as dormant after 300 minutes of inactivity.
Hello, I've been trying to solve this issue for 5 years now and after that time I have come up with a custom made framework. I can post here a link but it is still in development and not sure when I will release some stable version. Most of the API I have should be stable, but from time to time I'm forced to introduce a breaking change...
I would be thankful if you would share a high level overview how it works.
If you are finished with your post, please close it.
If you are not, please ignore this message.
Note that you will not be able to send further messages here after this post have been closed but you will be able to create new posts.
I have a documentation for it, but it is also in progress...
https://jfxsoft.gitlab.io/docs/getting_started/
Getting started - JFX soft - Documentation
Documentation for JFX soft framework
Can you elaborate on why you "need" to manage screens as opposed to creating one method capable of loading any given "screen" and calling that whenever you need that?
From my personal experience it is simply not enough to have one simple static method for loading screens. Maybe for very simple applications with very few screens it would be enough. But for something more complex I think it makes sense to have a more robust solution...
What exactly is not sufficient for you
I am sometimes using an abstract base class for controllers with custom init methods etc and then I more or less have static methods for loading
(or in principle code generation is possible as well)
but it depends on your exact requirements
Years ago (Java 8 was new)I found a very nice tutorial on YouTube for very simple managing screens. It was able to switch one screen into another one. It was also supporting animations between transitions from one screen to another.
But then I started building much more complex applications where I had screens built from other screens (single blocks) and then I realised it is not enough for me.
oh that's what you want to do
yeah for that you need some sort of classpath scanning or code generation
Form with a single button is not enough for business applications...
but you can do some very simple classpath scanning for that
I assume you are using FXML for all your screens?
Yep
Is it possible to create one folder (in your resources) where all of these FXML files are contained in and follow a naming convention such that no non-screen FXML files in that folder match the naming convention?
Then I started thinking about how to separate business logic from the view. So I had to distinguish between the view controller (bound to a view) and the "form" controller which contains logic+ services for real stuff...
Yeah, I have a single directory in resources for views. I went a bit further and separated views by logical part of the application
Then I have prepared a lot of "standard" predefined views with some minimal logic. So for example a login dialog with validation would be quite easy to create...
You should be able to do something like
And for resources, I got inspired by Android and have a plugin which discovers all resources and generates a class with paths to each resource so loading is also easy...
and then you can load the views and the controller with
FXMLLoader
normally
and if you want to use some sort of dependency injection for the business logic, that's an option as wellI could have resources placed in multiple java/maven modules so I have implemented a "discovery" process to allow load view from any module...
That's possible as well using things like the service loader API
Yeah, that's what I'm using 😁
you can implement something where classes can register themselves and these classes then know where you can find the FXML files
i.e. the classes know about the directories
or just that if all modules use the same class loader
I need to build a framework that facilitates quickly building Applications for internal use. Our requirements are pretty specific so existing frameworks would not help too much. (Certification processes are bitches)
I'll likely use Spring boot and services for it. Even if it seems overkill. But we need to evaluate spring for our backends anyways
What I said before still applies
Also do you need to certify every single small library you are adding?
I likely will have to.
You can do simple classpath scanning without external libraries as I've shown before
I've tried this before, but it did not work too good.
What was the issue?
I had the problem that my library was inside it's own package and I had a registration step where I would pass a class to my library to search the entire package of that class, but only views contained in the same package like the library were found.
I think you called getClass() on a library object or used
.class
if you use .getClass().getClassLoader()
, it should search from the classpath rootThat's what I did
and the same should apply when the path starts with a
/
and that's why it happened
by default, it uses the package of the class
you can use getClassLoader()
or start it with a /
Another weirdly mysterious Java detail 😅
it isn't that mysterious
it's literally in the docs
I tried with
getClassLoader()
too, but it did not work as I expected. It showed me files that it wasn't supposed to find and did not find ones I wanted it to find. But maybe my code was shit.Note:
Class#getResourceAsStream
doesn't work across modules I think
and even for ClassLoader#getResourceAsStream
, the package should be exported
Are the packages exported to the library module?Yes. But I would not trust my code in that case.
actually nvm it needs to be opened unconditionally
not
exports to
but opens
Maybe that was the weird part.
I exported them
I'll go and try to whip something up so I can test. But I'd like to not close the thread for now.
otherwise if you have access to a
Class
, you can use the getResourceAsStream
method on that Class
with a leading /
to accesses resources of that module
that should also work without opening/exporting I thinkResources in named modules are subject to the encapsulation rules specified by Module.getResourceAsStream. Additionally, and except for the special case where the resource has a name ending with ".class", this method will only find resources in packages of named modules when the package is opened unconditionally (even if the caller of this method is in the same module as the resource).https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/ClassLoader.html#getResource(java.lang.String)
ClassLoader (Java SE 21 & JDK 21)
declaration: module: java.base, package: java.lang, class: ClassLoader
actually the "opens" is also a requirement with
Class#getResourceAsStream
Resources in named modules are subject to the rules for encapsulation specified in the Module getResourceAsStream method and so this method returns null when the resource is a non-".class" resource in a package that is not open to the caller's module.https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Class.html#getResourceAsStream(java.lang.String)
Class (Java SE 21 & JDK 21)
declaration: module: java.base, package: java.lang, class: Class
but for that,
opens to
is sufficient💤
Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived.
If your question was not answered yet, feel free to re-open this post or create a new one.
In case your post is not getting any attention, you can try to use /help ping
.
Warning: abusing this will result in moderative actions taken against you.