13.2. Creating a Custom Component¶
Up to this point, you’ve been building apps using provided JavaFX components such as
TextFieldandImageView. 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
HBoxof the scene graph contains two separate, but identical,VBoxobjects. Building this app would require the programmer to repeat the exact same code to create these twoVBoxobjects. 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
VBoxsub-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
ImageLoadercomponent we will create. We want to avoid repeating this work by replacing this redundant part of the original scene graph with a singleImageLoadercomponent:|-- | 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 calledImageLoaderin thecs1302.guipackage that extends theVBoxclass (additional details are provided below; please read them carefully). As this class extendsVBox, it “is-a”VBoxand inherits all of the members ofVBox(although onlypublicandprotectedmembers will be directly visible).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.Your
ImageLoaderclass should contain instance variables for the nodes in the sub-graph above (HBox,TextField,Button, andImageView). You do not need an instance variable forVBoxbecause theImageLoaderitself is aVBox! For the most part, the required instance variables can be cut and paste 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.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, andButton). SinceImageLoaderextendsVBox, it “is-a”VBox. Therefore, you can call anyVBoxmethods 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 theVBoxnode 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, andButton) from the constructor andinitmethods ofImageApp. All of that code will now be run when we create a newImageLoaderobject. This includes the code to initialize theImageViewand 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
VBoxclass. This class is essentially aVBoxwith 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
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
HBoxinstance variable in theImageAppclass and instantiate it within the constructor. This will serve as the container for ourImageLoaderobjects. Set thespacingproperty of theHBoxto10by passing10into theHBoxconstructor. Now, in theinitmethod, add the twoImageLoaderobjects to theHBoxobject ofImageApp.Make sure you pass the reference to your newly created
HBoxobject into theSceneconstructor within thestartmethod ofImageApp. Previously, the root of our scene graph was aVBox. Now it’s anHBoxthat contains twoImageLoaderobjects.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
ImageLoadercomponent in class.