13.2. Creating a Custom Component¶
Up to this point, you’ve been building apps using provided JavaFX components such as
TextField
andImageView
. In this next set of steps, we will walk you through the creation of your own custom, reusable component based on the set of existing components contained in the application.Consider the following containment hierarchy:
--| Stage | | | Scene | |-- | | | HBox | | |\ | | | \------------------\ | | | | | | VBox VBox | Overall | / \ / \ | Containment Scene | / \ / \ | Hierarchy Graph | HBox ImageView HBox ImageView | | / \ / \ | | / \ / \ | | TextField Button TextField Button | |-- --|
In this scenario, the root
HBox
of the scene graph contains two separate, but identical,VBox
objects. Building this app would require the programmer to repeat the exact same code to create these twoVBox
objects. Now, imagine that there are hundreds of these in an app. Custom components allow us to avoid this type of redundancy!Consider the following scene graph where we replace the lower
VBox
sub-graphs withImageLoader
, a custom component we will create in the next step. The resulting scene graph is much cleaner (kinda looks like a giraffe, no?)!--| Stage | | | Scene | |-- | | Overall | HBox | Containment Scene | |\ | Hierarchy Graph | | \------------------\ | | | | | | ImageLoader ImageLoader | |-- --|
Now consider the sub-graph for the
ImageLoader
component we will create. We want to avoid repeating this work by replacing this redundant part of the original scene graph with a singleImageLoader
component:|-- | VBox | / \ Sub- | / \ Graph | HBox ImageView | / \ | / \ | TextField Button |--
Note that the root of this sub-graph is a
VBox
. With this in mind, create a class calledImageLoader
in thecs1302.gui
package that extends theVBox
class (additional details are provided below; please read them carefully). As this class extendsVBox
, it “is-a”VBox
and inherits all of the members ofVBox
(although onlypublic
andprotected
members will be directly visible).The class should contain the
static
constants from theImageApp
class. They can be cut and pasted directly from that class, perhaps changing them toprotected
visibility if you wish to do so. That way they can be accessed by the other classes in the package.Your
ImageLoader
class should contain instance variables for the nodes in the sub-graph above (HBox
,TextField
,Button
, andImageView
). You do not need an instance variable forVBox
because theImageLoader
itself is aVBox
! For the most part, the required instance variables can be cut and paste from theImageApp
class. Any instance variables that you move into theImageLoader
class can be removed fromImageApp
. You can also remove any imports that are no longer needed inImageApp
.In
ImageLoader
, add a default constructor that explicitly callssuper()
. After the call tosuper()
, the constructor should instantiate the other nodes in theImageLoader
sub-graph (HBox
,ImageView
,TextField
, andButton
). SinceImageLoader
extendsVBox
, it “is-a”VBox
. Therefore, you can call anyVBox
methods usingthis
as the calling object. Use this knowledge to add your newly created nodes to the sub-graph rooted atthis
similar to how they are added to theVBox
node in theImageApp
class. Your code will likely look something like the code below with additional statements to instantiate the components and connect them:public ImageLoader() { super(); // instantiate objects for the component's sub-graph, then // add them to the ImageLoader (i.e., this)... this.getChildren().addAll(urlLayer, imgView); } // ImageLoader
Remove the code to create the subgraph (
HBox
,ImageView
,TextField
, andButton
) from the constructor andinit
methods ofImageApp
. All of that code will now be run when we create a newImageLoader
object. This includes the code to initialize theImageView
and set up the button handler.Take a moment to think about what you are doing. You have created your own custom class that extends the JavaFX
VBox
class. This class is essentially aVBox
with some of the components built into it. Once we complete this class, we will be able to add objects of this class to a scene graph and all the messy details of creating that object will be hidden inside ofImageLoader
!Move the
loadImage
method fromImageApp
toImageLoader
. This is the method that is called when the button is clicked. Don’t forget to set the handler on yourImageLoader
’s button.You’ve probably noticed that
ImageApp
has significantly decreased in size. We moved a lot of that code over into our custom component! Now, instantiate two objects of typeImageLoader
within the constructor ofImageApp
.Create an
HBox
instance variable in theImageApp
class and instantiate it within the constructor. This will serve as the container for ourImageLoader
objects. Set thespacing
property of theHBox
to10
by passing10
into theHBox
constructor. Now, in theinit
method, add the twoImageLoader
objects to theHBox
object ofImageApp
.Make sure you pass the reference to your newly created
HBox
object into theScene
constructor within thestart
method ofImageApp
. Previously, the root of our scene graph was aVBox
. Now it’s anHBox
that contains twoImageLoader
objects.Compile and run your new app and load up a few 500x500 images. You should see something like the image below:
Imagine all the ways you could use your new custom component! Also, think of other custom components you could build by extending existing JavaFX components. We will explore more uses of the
ImageLoader
component in class.