User Data Main Master Calling Message Details |
All source code in this post is available on GitHub.
This video demonstrates the application. A ListView is filled with rows of Buttons. Each row starts with a Label and contains the same 3 commands: Edit, Run, Send. Pressing any of the command will bring up the details of the selection, an identifier I stored with UserData. Although, the identifier is a simple piece of data, this could grow to a complex, nested record coming from the Main view.
All of the code for the application is available on GitHub.
The Master view is an FXML file containing a ListView wrapped up in a VBox. See the following screenshot from SceneBuilder.
User Data App Contains a ListView |
@FXML ListView<Node> lvButtons;
lvButtons is a ListView of Nodes, specifically HBoxes.
To minimize the row-creation code, I'm driving the creation off of two data structures. The first data structure, buttonDefs, contains a Label and command argument that is used to specify the type of operation that will be executed. The second data structure, data, is a list of business records. For demonstration purposes, this is just a pair of items. However, you can swap out the simple ID String for a complex Java object.
Here is the code for buttonDefs
String[][] buttonDefs = { {"Edit", "Editting"}, {"Run", "Running"}, {"Send", "Sending"} };
Here is the code for data
String[][] data = { {"A", "ID: A001"}, {"B", "ID: B001"}, {"C", "ID: C001"}, {"D", "ID: D001"}, {"E", "ID: E001"} };
Next, I use a loop to iterate over the data, creating a row in the ListView for each record (A, B, C, etc). The loop adds an HBox and a Button of each type to the ListView. PROP_COMMAND_MESSAGE is a constant used to avoid a key mismatch between the put() and the get() call. commandHandler will follow.
for( String[] rec : data ) { HBox hbox = new HBox(); hbox.setSpacing(20.0d); Label label= new Label( rec[0] ); hbox.getChildren().add(label); for( String[] buttonDef : buttonDefs ) { Button btn = new Button(buttonDef[0]); btn.getProperties().put(PROP_COMMAND_MESSAGE, buttonDef[1]); btn.setUserData(rec[1]); btn.setOnAction(commandHandler); hbox.getChildren().add(btn); } lvButtons.getItems().add( hbox ); }
Finally, commandHandler is an onAction shared among each Button in each row.
private EventHandler<ActionEvent> commandHandler = (evt) -> { Node n = (Node)evt.getSource(); String id = (String) n.getUserData(); String commandMessage = (String)n.getProperties().get(PROP_COMMAND_MESSAGE); Alert alert = new Alert(Alert.AlertType.INFORMATION, commandMessage + " " + id); alert.showAndWait(); };
commandHandler receives its operands via the ActionEvent. The ActionEvent references a Button source which is loaded up with enough information to determine the type processing (commandMessage) and the source data (id). This can scale to an EventHandler that delegates the processing to another object, possibly supplied via buttonDefs.
Sometimes, I'll create a separate class for the EventHandler which uses fields to hold operands. That approach -- a separate class -- is better if you need to share the code outside of this Java file. In that case, you may still want to use Node Properties if you need to process the Scene graph outside of the handler. For example, all of the Buttons are tagged with a "commandMessage" Property which can be used for access control.
All source code in this post is available on GitHub.
No comments:
Post a Comment