Module 1 Task 2: Creating a Wall of Thumbnails


Launch JavaFX Media Browser Application Download NetBeans Project

Introduction

In Task 1 the application uses a single media file to create a thumbnail. The thumbnail is centered on a stage. Clicking the thumbnail displays a scaled-up version of the image and its file name (the only available metadata thus far). A second click hides the larger image.

In Task 2 you gather file metadata and use it to lay out thumbnails in a grid (a wall with three rows and many columns). The onMouseClicked behavior is the same as Task 1, except now the application centers the wall within a resizeable window, and centers the scaled-up thumbnail over the wall.

Running the Project

  1. Download the Module 1 Task 2 NetBeans project.

  2. Run the project. The wall is displayed as shown in Figure 1.

  3. Click a thumbnail to see the full size view with the file name.

  4. Click the full scale view to close the enlargement.

  5. Resize the window to see how the application adjusts. Note, you will create a scroll control in a future module.

Wall with thumbnails, unscaled Figure 1

Architecture

Task 1 lays the groundwork for image handling. You start with the CustomNodes Thumbnail.fx, Photo.fx, and Main.fx, and the class Constants.fx.

As shown in Figure 2, Task 2 adds a new class, MetaData.fx, and a new CustomNode, Wall.fx. The following diagram shows new or changed portions in blue, and lists some major differences. If you diff Task 1 and Task 2 source files you can see that using metadata affects all classes.

Architecture for Module 1 Task 2
Figure 2

Using the MetaData Class to Size and Position Thumbnails

MetaData (in MetaData.fx) creates a data model. This section reviews how MetaData is used throughout this task.

Creating a Data Model

Take a look at the new MetaData.fx CustomNode. MetaData is a container that stores the thumbnail URL, the media URL, and the file name in one place.

Source Code
package class MetaData {
    package var thumb_url : String;
    package var media_url : String;
    package var filename : String;
}

In Task 2 the media URL and the thumbnail URL have the same value, but the values can be different. The variable sampleMetaData is a Sequence of 60 MetaData objects, and Main.fx eventually references it to populate the wall. This test data will be replaced with web data in future tasks.

Using MetaData in Thumbnail.fx

Thumbnail is a CustomNode that encapsulates the MetaData and corresponding thumbnail image. To support metadata, references to the url variable have become references to the metaData variable.

In Thumbnail, the image variable is initialized with metaData.thumb_url. The PHOTO_PLACEHOLDER is displayed while the image is being loaded in the background.

Source Code
def image : Image = Image {
        url: metaData.thumb_url
        backgroundLoading: true
        placeholder: placeholder
     }

Using MetaData in Photo.fx

As seen in Module 1 Task 1 the Photo.fx CustomNode defines the ImageView variable to contain the full image. The ImageView variable is defined to be javafx.scene.image.ImageView. Image sizing and other image display actions operate on the ImageView container. Photo.fx also positions the filename metadata in relation to the image, puts a border around the image, and specifies a placeholder for the image.

Photo.fx makes the following small changes:

  • Declarations that used the "canned" URL in Task 1 now get the URL from the MetaData object.

  • Creates a new variable named metaData, where metaData is an object of type MetaData, as defined in MetaData.fx.

Note that the Photo.fx image definition is very similar to the definition in Thumbnail.fx. In Photo.fx the image URL is metaData.media_url, and in Thumbnail.fx the image URL is metaData.thumb_url.

Creating a Reflection Effect

Thumbnail.fx manages the thumbnail appearance so the reflection effect is defined here. The effect is applied when the Thumbnail is in the bottom row of the wall, as determined by Wall.fx. The reflect variable is a way for Wall.fx to tell the Thumbnail to apply the reflection effect.

Thumbnail imports classes javafx.scene.effect, the package that supports effects. The new variable reflect is used to apply the reflection to the Thumbnail. Effects can be applied to any Node and in this case the reflection is applied to the Thumbnail as a whole. The following passage defines the reflection settings.

Source Code
def reflection : Effect = Reflection {
    fraction: .6
    topOpacity: .6
    bottomOpacity: 0
    topOffset: 3
}

Note that reflection is not part of the Thumbnail class. reflection is a script-level variable because the values never change. The effect is initialized in the create() function such that the reflection only appears when reflect is true.

Source Code
effect: if ( reflect ) then reflection else null

Loading and Displaying Many Thumbnails

This section discusses the new class Wall.fx and how Main.fx uses metadata to push thumbnails onto the Wall.

Creating and Positioning Thumbnail Instances

Wall.fx is a CustomNode that is the container for the grid of thumbnails. It collects metadata and creates the variable thumbnails to hold images for the wall. Wall then uses x and y coordinate translation to lay out the wall. Open Wall.fx and take a few minutes to read the comments. Wall is created in Main.

As new data arrives, it pushes metadata into the MetaData object and the images load onto the wall. Wall contains a sequence of metaData. The metaData sequence has a visual representation in the thumbnails that appear in the Wall. The metaData sequence is initialized in Main to be a sequence of sample data. Later, the metadata will come from a web search API.

Source Code
package var metaData : MetaData[]

Take a look at the variable fullView - it's a variable that is a function:

Source Code
package var fullView : function(metaData : MetaData, placeholder : Image) : Void;

fullView is called when the user clicks on a thumbnail. This function allows Thumbnail to be totally ignorant of how the full view of the thumbnail gets displayed. This is a good design goal since you can change Photo without worrying about what's going on in Thumbnail.

Note: The fullView implementation is in Main.fx, but since thumbnails are created in Wall.fx, the fullView variable must be initialized as shown above. In practice, when Main creates wall, it initializes Wall's fullView variable and Wall uses that to initialize Thumbnail. This "cross referencing" can be avoided if you move fullView out of Main and into Wall.

The following code positions the thumbnail.

Source Code
 translateX: (col * Constants.THUMB_WIDTH) +
                   (col * Constants.THUMB_HORIZONTAL_SPACING)
 translateY: (row * Constants.THUMB_HEIGHT) +
                   (row * Constants.THUMB_VERTICAL_SPACING)

Bringing It All Together in Main.fx

Main.fx already handles user interaction with a thumbnail. In this task the application does the following:

  • References MetaData rather than a URL
  • Adds the wall to the stage, placing it in the center
  • Uses MetaData to populate the wall with images

Main.fx is well commented, so take some time to read the comments and examine the code.

Note how the javafx.scene.Node variable boundsInLocal is used to position the photo on the wall, and the wall on the scene. In this application width and height are the only important factors, and they are going to be the same in the parent, the scene and the screen. Using boundsInLocal avoids the additional overhead of computing boundsInParent, boundsInScene, or boundsInScreen. If it is necessary to use x and y coordinates, you might need to use one of the other boundsIn* variables.

Try It

  • In Constants.fx, try changing THUMB_WIDTH, THUMB_HEIGHT, THUMB_VERTICAL_SPACING, and THUMB_HORIZONTAL_SPACING. Run the project to see the result.

  • In Thumbnail.fx set backgroundLoading to false. Run the project and note the behavior change. Set backgroundLoading to true once you are done.

  • Try editing Constants.fx to change the value of THUMB_ROWS, and run the project. You might have to resize the window to view your results.

  • Try changing the reflection settings mentioned in Creating a Reflection Effect and run the project to see the results.

  • Try some of the other effects in the javafx.scene.effects package. Note that many of the effects can use other effects as input, so try combining more than one effect.

Rate This Article
Discussion

We welcome your participation in our community. Please keep your comments civil and on point. You may optionally provide your email address to be notified of replies—your information is not used for any other purpose. By submitting a comment, you agree to these Terms of Use.

 

English
日本語
한국어
简体中文
русский
Português do Brasil