I've got the task to program a graphical network editor application as a university project. For that I need three types of items/controls. A circle shape, a rectangular shape and arrows to connect the other shapes (the whole thing works somewhat like MS Visio in some ways). The shapes/controls need some additional features like moving, scaling a context menu etc. Also I need to have full control over the graphical representation of these objects i.e. I want to 'draw' them myself or at least be able to modify them as I need.
I am using JavaFX and have little to no experience with it. So I was wondering, what would be the best way to implement these custom controls. It is required to use JDK 7, so using SkinBase
and BehaviourBase
is not an option, since they are private before JDK 8.
I was thinking about subclassing Path
or Canvas
to use as my controls. But I know too little about the implications to make an informed decision.
Could someone give me some advice, which base classes to consider and what implications that might have?
Thx alot.
This answer is just going to be advice, there is no real right or wrong answer here. Advice is necessarily opinionated and not applicable to all situations, if you don't agree with it, or it doesn't apply to your situation, just ignore it.
Sorry for the length, but the question is open-ended and the potential answer is complicated.
I was thinking about subclassing Path or Canvas to use as my controls.
Don't. Favor composition over inheritance. Have a control class which implements the functional interface of the control and works regardless of whatever UI technology is behind it. Provide the control class a reference to a skin class which is the UI representation of the control. The skin will specify how to render the control in a given state (getting the control state from the associated control object). Also, the skin will be the thing which responds to user manipulations, such as a mouse presses and instructs the control to change its state based upon the mouse press - so the skin knows what control it is associated with and vice versa. In the control class have bindable properties to represent the control state.
A simple example of this is the Square and SquareSkin from this tic-tac-toe game implementation.
For example, imagine a checkbox control. The bindable property of control might be an enum with states of (checked, unchecked, undetermined). A skin might render the checkbox as a square box with a tick mark to represent the checked state. Or maybe the skin will render a rounded edge with an X. The skin could use whatever technology you want to render the checkbox, e.g. a canvas or a collection of nodes. The skin registers listeners for mouse clicks, key presses, etc. and tells the checkbox control to set its check state. It also has a listener on the check state and will choose whether to render the check tick based on that state.
The key thing is that the API interface to the checkbox is just a checkbox control class with a check state. The way the UI is handled is abstracted away from the checkbox API, so you can swap the UI implementation in and out however you want without changing any other code.
Subclassing Path is quite different from subclassing Canvas. For the situation subscribe, I would definitely favor subclassing a Shape node (or Region) as opposed to a Canvas. Using Nodes, you automatically have a really rich UI rendering and event model, bindable property set and and painting framework which you won't get with a Canvas. If you don't use Nodes, you will likely end up trying to re-create and build some parts of the Node functionality in some sub-standard way. Canvas is great for things like porting 2D games or graphing engines from other frameworks or building things that are pixel manipulators like a particle system, but otherwise avoid it.
Consider using a Layout Pane for your control skin, e.g. a StackPane or something like that. The layout panes are containers, so you can place stuff inside them and use aggregation and composition to build up more complex controls. Layout panes can also help with laying out your nodes.
Anything which subclasses a region like a Pane can be styled via CSS into arbitrary shapes and colors. This is actually how the in-built checkbox (and other controls) in JavaFX work. The checkbox is a stack of two regions, one the box and the other the check. Both are styled in CSS - search for .check-box in the modena.css stylesheet for JavaFX. Note how -fx-shape
is used to get the tick shape by specifying an svg path (which you can create in an svg editor such as inkscape). Also note how background layering is used to get things like focus rings. The advantage of using CSS for style is that you can stylistically change your controls without touching your Java code.
so using SkinBase and BehaviourBase is not an option
Even though you have decided to not use these base classes (which I think is an OK decision even if you are targeting only Java 8+), I think it is worthwhile studying the design of the controls in JavaFX source. Study button or checkbox, so you can see how the experts do these kind of things. Also note that such implementations may be overkill for your application because you aren't building a reusable control library. You just need something which will work well and simply within the confines of your application (this is the reason I don't necessarily recommend extending SkinBase for all applications).
As a minimum read up on Controls on the open-jfx wiki.
It is required to use JDK 7
IMO, there is not much point in developing a new JavaFX application to target Java 7. There were many bug fixes for Java 8, plus new and useful features added. In general, Java 7 is a sub-optimal target platform for JavaFX applications. If you package your application as a self-contained application, then you can ship whatever Java platform you want with your application, so the target platform doesn't matter.
IMO deployment technologies such as WebStart or browser embedded applications (applets), which can make use of pre-installed java runtimes on a system, are legacy deployment modes which are best avoided for most applications.
Still, there might be a situation where you absolutely must have Java 7 due to some constraints outside your control, so I guess just evaluate carefully against your situation. If you have such constraints and must build on a stable, legacy UI toolkit which works with runtimes released years ago, you could always use Swing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With