13.2. Creating a Custom Component¶
Up to this point, you've been building apps using provided JavaFX components such as TextField and ImageView. 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 containment hierarchy (and scene graph) depicted in Fig. 13.3:
Fig. 13.3 Containment Hierarchy: Multiple Image Loaders¶
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 code to create these two VBox 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 with
ImageLoader, a custom component we will create in the next step. The resulting scene graph is much cleaner.Fig. 13.4 Containment Hierarchy: Multiple Image Loaders¶
Now consider the sub-graph for the
ImageLoadercomponent we will create. We want to avoid repeating this work by replacing this redundant part of the original scene graph with a singleImageLoadercomponent:Fig. 13.5 Sub-Graph: Before a Component¶
Note that the root of this sub-graph is a VBox. With this in mind, create a class called
ImageLoaderin thecs1302.guipackage that extends the VBox class (additional details are provided below; please read them carefully). As this class extends VBox, it "is-a" VBox and inherits all of the members of VBox (although onlypublicandprotectedmembers will be directly visible).Fig. 13.6 Class Hierarchy: After a Component¶
The class should contain the
staticconstants from theImageAppclass. They can be cut and pasted directly from that class, perhaps changing them toprotectedvisibility if you wish to do so. That way they can be accessed by the other classes in the package.Fig. 13.7 Sub-Graph: After ImageLoader¶
Your
ImageLoaderclass should contain instance variables for the nodes in the sub-graph above (HBox, TextField, Button, and ImageView). You do not need an instance variable for VBox because theImageLoaderitself is a VBox! For the most part, the required instance variables can be cut and pasted from theImageAppclass. Any instance variables that you move into theImageLoaderclass can be removed fromImageApp. You can also remove any imports that are no longer needed inImageApp.Fig. 13.8 Class Hierarchy: After a Component¶
In
ImageLoader, add a default constructor that explicitly callssuper(). After the call tosuper(), the constructor should instantiate the other nodes in theImageLoadersub-graph (HBox, ImageView, TextField, and Button). SinceImageLoaderextends VBox, it "is-a" VBox. Therefore, you can call any VBox methods usingthisas the calling object. Use this knowledge to add your newly created nodes to the sub-graph rooted atthissimilar to how they are added to the VBox node in theImageAppclass. 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, and Button) from the constructor and
initmethods ofImageApp. All of that code will now be run when we create a newImageLoaderobject. This includes the code to initialize the ImageView 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 a VBox 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 of
ImageLoader.Move the
loadImagemethod fromImageApptoImageLoader. 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
ImageApphas significantly decreased in size. We moved a lot of that code over into our custom component. Now, instantiate two objects of typeImageLoaderwithin the constructor ofImageApp.Create an HBox instance variable in the
ImageAppclass and instantiate it within the constructor. This will serve as the container for ourImageLoaderobjects. Set thespacingproperty of the HBox to10by passing10into the HBox constructor. Now, in theinitmethod, add the twoImageLoaderobjects to the HBox object ofImageApp.Make sure you pass the reference to your newly created HBox object into the Scene constructor within the
startmethod ofImageApp. Previously, the root of our scene graph was a VBox. Now it's an HBox that contains twoImageLoaderobjects.Compile and run your new app and load up a few 500x500 images. You should see something like the image below:
Fig. 13.9 Screenshot: ImageApp¶
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
ImageLoadercomponent in class.