(Java language, IDE NetBeans 8.0.2, *IMPORTANT - JFrames cannot be used it must
ID: 670580 • Letter: #
Question
(Java language, IDE NetBeans 8.0.2, *IMPORTANT - JFrames cannot be used it must be with JavaFx controls, i know this is harder but this is the basics*) -
My Photos :
create a photo album application. The application will load a collection of images and displays them in a photo album layout (im not sure how this part works if you could explain more on how to have the application load a collection of images and how it displays them.). The program will allow the user to tag images with metadata:
• Title for the photo. Limited to 100 characters. (these specifics seem to be hard i might need a very experienced programmer to answer this question)
• Description for the photo. Limited to 300 characters.
• Date taken
• Place taken
Functional Specification : (these specifications are very important and im not sure how to do these.)
1. The album should be able to display the pictures sorted by Title, Date, and Place.
2. When the user clicks on a photo, the metadata should be displayed. (i really dont understand this part)
3. The user should be able to add or remove photos from the album
*/ could you add comments to whats going on? this is how i best learn is seeing a working code and dissecting the code to figure out what is going on. /*
Explanation / Answer
SearchDemo.java
---------------
package demos.search;
import java.stage.Stage;
public calss SearchDemo extends Applicat@Override
public void start(Stage primaryStage)
parent root=FXMLLoader.load(getClass().getResource("search_demo.fxml")
primaryStage.setScene(SceneBuilder.create().root(root).build()
);
search_demo.fxml
-----------------
<?xml version="1.0" encodeing="UTF-8"?>
<?import javafx.scene.layout.*?>
<BorderPane fx:controller="demos.search.SearchDemoController">
<top>
<BorderPane>
<left>
<HBOX spacing="6"searchTerm
<Button fx:id="searchButton"
> -$handleSearchAction"/>
SearchDemoController.java
-------------------------
package demos.search;
import javafx.fxml.Initializable;
public class SearchDemoController extends Initializable
{
@FXML private Button searchButton
@Override
public void initialize(URL location)
ResourceBundle resources) {...}
}
@FXML
protected void handleSearchAction(ActionEvent event){
SearchDemo.java is the main module of our JavaFX application, extending Application and containing main() and start() methods. Its role in the SearchDemoFXML application is to create a scene and populate it with the scene graph obtained from the search_demo.fxml file via the FXMLLoader.load() method. This produces the UI shown in the lower left of Figure 6.
The search_demo.fxml file in the top middle of Figure 6 contains an XML representation of the scene graph. It also specifies that the SearchDemoController.java file is a controller class, and it delegates events that occur to UI components to handler methods in the controller.
The SearchDemoController controller class in the bottom middle of Figure 6 holds references to UI components expressed in search_demo.fxml and handles events that occur in them.
Turning to the right side of Figure 6, the SearchDemoController invokes methods of the RestFX classes to query the iTunes services' REST interface. RestFX is a library external to JavaFX 2.0 that is leveraged here for communication with the iTunes REST endpoints and parses its JSON responses. The "See Also" section at the end of this article has a link to the REST/FX project.
Bootstrapping an FXML Application
As pointed out in the previous section, SearchDemo.java is the main module of our JavaFX application, as shown in Listing 1:
package demos.search;
import java.util.ResourceBundle;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.SceneBuilder;
import javafx.stage.Stage;
public class SearchDemo extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("search_demo.fxml"),
ResourceBundle.getBundle("demos/search/search_demo"));
primaryStage.setTitle("Search Demo");
primaryStage.setWidth(650);
primaryStage.setHeight(500);
primaryStage.setScene(
SceneBuilder.create()
.root(root)
.build()
);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Listing 1: SearchDemo.java
The FXMLLoader.load() invocation shown in Listing 1 accepts two arguments:
java.net.URL, which represents the FXML document (in this case, search_demo.fxml)
ResourceBundle, which contains strings used in our application
Note that the FXMLLoader.load() method has other signatures, including those that don’t specify a ResourceBundle.
After the FXML document is loaded, the scene graph is instantiated and assigned to the root property of the scene, which is associated with the stage and made visible with its show() method.
Expressing the UI with FXML
As shown in Figure 6, the search_demo.fxml file contains an XML representation of the scene graph. Let’s take a closer look at this file, which is shown in Listing 2:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import demos.search.*?>
<BorderPane fx:controller="demos.search.SearchDemoController"
xmlns:fx="http://javafx.com/fxml">
<top>
<BorderPane>
<left>
<HBox spacing="6">
<children>
<TextField fx:id="searchTermTextField" prefColumnCount="18"
/>
<Button fx:id="searchButton" disable="false"
/>
</children>
</HBox>
</left>
<right>
<Label fx:id="statusLabel"/>
</right>
</BorderPane>
</top>
<center>
<BorderPane>
<center>
<TableView fx:id="resultsTableView">
<fx:define>
<ResultCellValueFactory fx:id="resultCellValueFactory"/>
</fx:define>
<columns>
<ResultTableColumn
key="itemName" text="%name" prefWidth="170"
cellValueFactory="$resultCellValueFactory"/>
<ResultTableColumn
key="itemParentName" text="%album" prefWidth="170"
cellValueFactory="$resultCellValueFactory"/>
<ResultTableColumn
key="artistName" text="%artist" prefWidth="170"
cellValueFactory="$resultCellValueFactory"/>
</columns>
</TableView>
</center>
<right>
<VBox alignment="topCenter" spacing="6">
<children>
<StackPane prefWidth="120" prefHeight="120"
>
<children>
<ImageView fx:id="artworkImageView"/>
</children>
</StackPane>
<Button fx:id="previewButton" text="%preview"
/>
</children>
</VBox>
</right>
</BorderPane>
</center>
</BorderPane>
Listing 2: search_demo.fxml
Understanding the Structure of our FXML Document
As shown in Listing 2, our FXML document consists of some import processing instructions and instance declarations. The import processing instructions are analogous to Java import statements, which prevent the need for class names to be qualified by package names.
The instance declarations express the scene graph hierarchically, including nodes such as layout containers, shapes, and UI controls. Note that the root node (in this case, a BorderPane) contains an attribute named fx:controller. This specifies that a class named demos.search.SearchDemoController will serve as the controller for this FXML document. We’ll take a closer look at the controller a bit later.
Using Instance Declarations
An instance declaration is characterized by an element that begins with an uppercase letter, optional attributes that begin with lowercase letters, and optional attributes represented as nested elements beginning with lowercase letters. For example, the snippet below from Listing 2 causes an HBox to be instantiated with its spacing property set to 6 and its style property set to -fx-padding: 0 0 6 0. In addition, a couple of children (a TextField and a Button) will be instantiated and contained by the HBox.
<HBox spacing="6">
<children>
<TextField fx:id="searchTermTextField" prefColumnCount="18"
/>
<Button fx:id="searchButton" disable="false"
/>
</children>
</HBox>
Note that any class that adheres to JavaBean conventions (for example, a no-argument constructor and get/set methods) may be used in an instance declaration.
Mapping Between the FXML Document and the Controller
Taking another look at the previous snippet from Listing 2, notice that the Button instance declaration contains the fx:id and onAction attributes. The fx:id attribute is assigned the "searchButton" string, which maps to the searchButton instance variable in the SearchDemoController class.
The onAction attribute is assigned the "#handleSearchAction" string, which maps to the handleSearchAction() method in the SearchDemoController class.
The searchButton instance variable and the handleSearchAction() method are shown in the following snippet from Listing 4 (which you’ll see in the "Defining the Controller" section in a moment).
public class SearchDemoController implements Initializable {
@FXML private Button searchButton;
...
@FXML
protected void handleSearchAction(ActionEvent event) {...}
...
}
This mapping enables the controller to manage the state of the instances expressed in the FXML document, as well as to handle events that occur in them.
Mapping Attributes to Resource Names in a ResourceBundle
The code in Listing 3, which is another snippet from Listing 2, demonstrates how to map the value of an attribute in an instance declaration to a resource name in a ResourceBundle. When the value assigned to an attribute begins with a percent character (%) and the FXMLLoader.load() invocation is supplied with a ResourceBundle, the locale-specific values will be used in place of the resource names.
<TableView fx:id="resultsTableView">
<fx:define>
<ResultCellValueFactory fx:id="resultCellValueFactory"/>
</fx:define>
<columns>
<ResultTableColumn
key="itemName" text="%name" prefWidth="170"
cellValueFactory="$resultCellValueFactory"/>
<ResultTableColumn
key="itemParentName" text="%album" prefWidth="170"
cellValueFactory="$resultCellValueFactory"/>
<ResultTableColumn
key="artistName" text="%artist" prefWidth="170"
cellValueFactory="$resultCellValueFactory"/>
</columns>
</TableView>
Listing 3: Example of Mapping Attributes to Resource Names
In our example, the TableView column headers are retrieved at startup from the resource names name, album, and artist located in the search_demo.properties file.
Creating an Object Outside of the Scene Graph
Taking another look at the snippet in Listing 3, you’ll see an fx:define element. This element is being used to create a ResultCellValueFactory object and to define a variable named resultCellValueFactory that refers to it. This object is then used in each of the ResultTableColumn instantiations, with the $ signifying that this is a variable reference.
A classic use of the fx:define element is to create a ToggleGroup, which isn’t a scene graph node, and to supply that ToggleGroup object to multiple radio buttons for mutually exclusive selection behavior.
Let’s turn our attention away from the FXML document and toward the controller.
Defining the Controller
As you saw earlier, instances declared in an FXML document can be mapped to variables in the controller, and events that occur in those instances can be mapped to handlers in the controller. Go ahead and peruse Listing 4, which contains the code for our controller, and we’ll discuss more relevant FXML concepts afterward.
package demos.search;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import restfx.web.GetQuery;
import restfx.web.Query;
import restfx.web.QueryListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class SearchDemoController implements Initializable {
@FXML private TextField searchTermTextField;
@FXML private Button searchButton;
@FXML private Label statusLabel;
@FXML private TableView<Map<String, Object>> resultsTableView;
@FXML private ImageView artworkImageView;
@FXML private Button previewButton;
private ResourceBundle resources = null;
private GetQuery getQuery = null;
public static final String QUERY_HOSTNAME =
"ax.phobos.apple.com.edgesuite.net";
public static final String BASE_QUERY_PATH =
"/WebObjects/MZStoreServices.woa/wa/itmsSearch";
public static final String MEDIA = "music";
public static final int LIMIT = 100;
public static final ImageView SEARCH_IMAGE_VIEW;
public static final ImageView CANCEL_IMAGE_VIEW;
static {
SEARCH_IMAGE_VIEW = new ImageView(new Image(SearchDemo.class
.getResourceAsStream("magnifier.png")));
CANCEL_IMAGE_VIEW = new ImageView(new Image(SearchDemo.class
.getResourceAsStream("bullet_cross.png")));
}
@Override
@SuppressWarnings("rawtypes")
public void initialize(URL location, ResourceBundle resources) {
this.resources = resources;
// Initialize the search button content
searchButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
searchButton.setGraphic(SEARCH_IMAGE_VIEW);
// Add a selection change listener to the table view
resultsTableView.getSelectionModel().getSelectedCells()
.addListener(new ListChangeListener<TablePosition>() {
@Override
public void onChanged(Change<? extends TablePosition> change) {
while (change.next()) {
if (change.wasAdded()) {
updateArtwork(change.getAddedSubList().get(0).getRow());
}
}
}
});
// Do an example initial search so that the table is populated on startup
searchTermTextField.setText("Cheap Trick");
handleSearchAction(null);
}
@FXML
protected void handleSearchAction(ActionEvent event) {
if (getQuery == null) {
String searchTerms = searchTermTextField.getText();
if (searchTerms.length() > 0) {
getQuery = new GetQuery(QUERY_HOSTNAME, BASE_QUERY_PATH);
getQuery.getParameters().put("term", searchTerms);
getQuery.getParameters().put("media", MEDIA);
getQuery.getParameters().put("limit", Integer.toString(LIMIT));
getQuery.getParameters().put("output", "json");
System.out.println(getQuery.getLocation());
statusLabel.setText(resources.getString("searching"));
updateActivityState();
getQuery.execute(new QueryListener<Object>() {
@Override
@SuppressWarnings("unchecked")
public void queryExecuted(Query<Object> task) {
if (task == getQuery) {
if (task.isCancelled()) {
statusLabel.setText(resources.getString("cancelled"));
searchTermTextField.requestFocus();
}
else {
Throwable exception = task.getException();
if (exception == null) {
Map<String, Object> value =
(Map<String, Object>)task.getValue();
List<Object> results = (List<Object>)value.get("results");
// Update the table data
ObservableList<?> items =
FXCollections.observableList(results);
resultsTableView.setItems(
(ObservableList<Map<String, Object>>)items);
statusLabel.setText(String.format(resources
.getString("resultCountFormat"), results.size()));
if (results.size() > 0) {
resultsTableView.getSelectionModel().select(0);
resultsTableView.requestFocus();
}
else {
searchTermTextField.requestFocus();
}
}
else {
statusLabel.setText(exception.getMessage());
searchTermTextField.requestFocus();
}
}
getQuery = null;
searchButton.setDisable(false);
updateActivityState();
}
}
});
}
}
else {
getQuery.cancel(true);
searchButton.setDisable(true);
statusLabel.setText(resources.getString("aborting"));
}
}
@FXML
protected void handlePreviewAction(ActionEvent event) {
Map<String, Object> selectedResult =
resultsTableView.getSelectionModel().getSelectedItem();
URL url;
try {
url = new URL((String)selectedResult.get("previewUrl"));
}
catch (MalformedURLException exception) {
throw new RuntimeException(exception);
}
try {
java.awt.Desktop.getDesktop().browse(url.toURI());
}
catch (URISyntaxException exception) {
throw new RuntimeException(exception);
}
catch (IOException exception) {
throw new RuntimeException(exception);
}
}
private void updateActivityState() {
boolean active = (getQuery != null);
searchTermTextField.setDisable(active);
searchButton.setGraphic(active ? CANCEL_IMAGE_VIEW : SEARCH_IMAGE_VIEW);
}
private void updateArtwork(int index) {
Map<String, Object> result = resultsTableView.getItems().get(index);
String artworkURL;
if (result == null) {
artworkURL = null;
previewButton.setDisable(true);
}
else {
artworkURL = (String)result.get("artworkUrl100");
System.out.println(result.get("itemName"));
previewButton.setDisable(false);
}
artworkImageView.setImage(artworkURL == null ?
null : new Image(artworkURL));
}
}
Listing 4: SearchDemoController.java
Initializing the Controller
As shown in Figure 1, when the SearchDemoFXML program starts, the table is initially populated from a search for songs by the rock band Cheap Trick. This is accomplished in the initialize() method of the controller shown in Listing 4, specified by the optional Initializable interface. When the controller implements Initializable, the initialize() method is invoked after instantiation and supplied with the URL of the FXML document and a reference to a ResourceBundle , if a reference was loaded by FXMLLoader.load().
As shown in Listing 5 (which is a snippet from Listing 4), other initialization that is performed by our SearchDemoController includes the following:
Assigning the supplied ResourceBundle reference to an instance variable
Modifying the appearance of the Button referenced by searchButton
Adding a change listener to the table so that the artwork image can be updated when the selected row in the table changes
public void initialize(URL location, ResourceBundle resources) {
this.resources = resources;
// Initialize the search button content
searchButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
searchButton.setGraphic(SEARCH_IMAGE_VIEW);
// Add a selection change listener to the table view
resultsTableView.getSelectionModel().getSelectedCells()
.addListener(new ListChangeListener<TablePosition>() {
@Override
public void onChanged(Change<? extends TablePosition> change) {
while (change.next()) {
if (change.wasAdded()) {
updateArtwork(change.getAddedSubList().get(0).getRow());
}
}
}
});
Listing 5: Initialization Performed by SearchDemoController.java
Handling Events
In the "Mapping Between the FXML Document and the Controller" section, we saw how to map instances and event handling between the FXML document and the controller. Listing 6 (which is another snippet from Listing 4) contains some portions of the event handler in our controller for when the user clicks the Search button.
@FXML
protected void handleSearchAction(ActionEvent event) {
...
String searchTerms = searchTermTextField.getText();
...
if (searchTerms.length() > 0) {
statusLabel.setText(resources.getString("searching"));
...
resultsTableView.setItems(
(ObservableList<Map<String, Object>>)items);
statusLabel.setText(String.format(resources
.getString("resultCountFormat"), results.size()));
if (results.size() > 0) {
resultsTableView.getSelectionModel().select(0);
resultsTableView.requestFocus();
}
else {
searchTermTextField.requestFocus();
}
Listing 6: Portions of the Event Handler from SearchDemoController.java
The code in Listing 6 demonstrates interaction by the controller with nodes in the scene graph expressed by the FXML document. For example, the search terms are obtained from the searchTermTextField, and the statusLabel text is set to the locale-specific value for the “searching” resource name. In addition, the table is populated with search results, its first row is selected, and keyboard focus is requested.
Related Questions
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.