Java F X With Multiple F X M L Files

Joe

Thành viên VIP
21/1/13
2,935
1,304
113
Hi everyone

Today I show you how to build a JavaFX application with Multiple FXML. As you know, JFX-App can be either a monolith or a Model-View-Control (MVC). A monolithic Application is usually a straight-forward development and does have some advantages, too. However, the greatest disadvantage is the enhancement/correction problems. It usually requires a "redesign" and "recompile" of the whole app.

MVC development is on the other hand flexible, but it could be confusing, too. Too many (little) modules could mess an overview of the whole. Especially when your app requires different models which are controlled and viewed by different FXML files. I show you today how to create an app with 4 different FXMLs and Controllers.



There are different ways to implement a multiple MVC-JFX application. The simplest way is to implement a principal controller which controls the main FXML view and the other controllers. Each Sub-Controller controls its own FXML View Model which is independent from each other. This way allows you to modify or to enhance a view model without having to redesign or to recompile the other modules. I show you a ZOO-MVC which consists of a main controller and 3 sub-controllers with their own FXML-View-Model.

The multiple FXML mechanism works as following:

- Main View (fxml/controller) instantiates all other Sub-ViewModels
- Each Sub-ViewModel works independently from other Sub-ViewModel, but it can initiate or set up some parts of other Sub-ViewModels.

The JFX Zoo.java app starts with its ZooController.java which presents the view zoo.fxml model of 3 Tabs: Animals, About Animal and Video. Each Tab represents a new ViewModel and Controller (animal.fxml/AnimalController.java, info.fxml/InfoController.java, video.xml/VideoController.java). The starting Tab is the Animals Tab (with 2 columns: animal names and image.) If an animal is selected (e.g. Mustang) ZooController starts to initiate the 2 other Tabs (About Animal and Video) according to the specified animal.

The ZooController
PHP:
public class ZooController {
 
	@FXML private InfoController infoController;		// 1st. controller
	@FXML private VideoController videoController;	  // 2nd. controller
	@FXML private AnimalController animalController;	// 3rd. controller
 
	@FXML private void initialize() {
		animalController.injectAnimalController(this);
	}
	/*
	Main controller controls the 3 other controllers.
	*/
	@FXML
	private void mouseClicked(MouseEvent e) {
		animalController.setEvent(e);
	}
	public void setInfo(String url ) {
		infoController.setInfo(url);
	}
	public void setVideo(String url) {
		videoController.setVideo(url);
	}
}
and its View FXML model
PHP:
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"
			minWidth="-Infinity" prefHeight="470.0" prefWidth="800.0"
			xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
			fx:controller="ZooController">
   <children>
	  <TabPane layoutX="127.0" layoutY="90.0"
			   tabClosingPolicy="UNAVAILABLE" AnchorPane.bottomAnchor="0.0"
			   AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"
			   AnchorPane.topAnchor="0.0" onMouseClicked="#mouseClicked">
		<tabs>
		  <Tab text="Animals">
			   <content>
				  <fx:include fx:id="animal" source="Animal.fxml" />
			   </content>
		  </Tab>
		  <Tab text="About Animal">
			   <content>
				  <fx:include fx:id="info" source="Info.fxml" />
			   </content>
		  </Tab>
		  <Tab text="Video">
			   <content>
				  <fx:include fx:id="video" source="Video.fxml" />
			   </content>
		  </Tab>
		</tabs>
	  </TabPane>
   </children>
</AnchorPane>
The AnimalController manages a list (zoolist.txt) of registered animals (name, link to Wikipedia, link to an image and link to the according Youtube or link to your own clip) with two possibilities:
- ADD: add a new animal to the list. Here you can see how a multiple TextField-Dialog works.
- DEL: delete an animal from the list.
PHP:
public class AnimalController {
 
  @FXML private Pane infoPane;
  @FXML private TextArea infoText;
  @FXML private ListView<String> animalList;
 
  public void injectAnimalController(ZooController zooController){
	this.zooController = zooController;
  }
 
  @FXML
  public void initialize() {
	urlList = new ArrayList<String>();
	picList = new ArrayList<String>();
	videoList = new ArrayList<String>();
	animals = FXCollections.observableArrayList();
	try {
	  byte[] cont = Files.readAllBytes((new File("ZooList.txt")).toPath());
	  String[] s = (new String(cont)).split(sp);
	  for (int n = 0; n < s.length; ++n) {
		String[] e = s[n].split("!");
		animals.add(e[0]);
		urlList.add(n, e[1]);
		picList.add(n, e[2]);
		videoList.add(n, e[3]);
	  }
	} catch (Exception ex) {
	  ...
	}
	animalList.setItems(animals);
  }
 
  public void setEvent(MouseEvent e) {
	//System.out.println("X="+e.getX()+", Y="+e.getY());
  }
 
  @FXML
  private void addAnimal(ActionEvent e) {
	lst = getList();
	if (lst != null && !lst.get(0).isEmpty()) {
	  animals.add(lst.get(0));
	  picList.add(lst.get(1));
	  urlList.add(lst.get(2));
	  videoList.add(lst.get(3));
	  try (FileOutputStream fout = new FileOutputStream("ZooList.txt", true)) {
		fout.write((lst.get(0)+"!"+lst.get(1)+"!"+lst.get(2)+"!"+lst.get(3)+sp).getBytes());
		fout.flush();
		fout.close();
	  } catch (Exception ex) {}
	}
  }
 
  @FXML
  private void delAnimal(ActionEvent e) {
	TextInputDialog dialog = new TextInputDialog("AnimalName");
	dialog.setTitle("Delete AnimalName");
	dialog.setHeaderText("Remove an AnimalName from ZOO");
	dialog.setContentText("AnimalName:");
	// Traditional way to get the response value.
	Optional<String> result = dialog.showAndWait();
	if (result.isPresent()){
	  ...
	}
  }
 
  @FXML
  private void onMouseClicked(MouseEvent e) {
	infoPane.getChildren().clear();
	int idx = animalList.getSelectionModel().getSelectedIndex();
	if (idx >= 0) {
	  // synchronize Tab "About Animal"
	  zooController.setInfo(urlList.get(idx));
	  // synchronize Tab "Video"
	  zooController.setVideo(videoList.get(idx));
	  // Image Tab
	  try {
		ImageView imgView = new ImageView(new Image(picList.get(idx)));
		infoPane.getChildren().add(imgView);
		imgView.fitWidthProperty().bind(infoPane.widthProperty());
		imgView.fitHeightProperty().bind(infoPane.heightProperty());
	  } catch (Exception ex) {
		ex.printStackTrace();
	  }
	}
  }
  private ArrayList<String> getList() {
	ArrayList<String> list = new ArrayList<String>();
	Dialog<List<String>> dialog = new Dialog<>();
	dialog.setTitle("Add New Animal");
	dialog.setHeaderText("Links to Image, Wiki and Youtube");
 
	// Set the button types.
	ButtonType aBut = new ButtonType("ADD", ButtonData.OK_DONE);
	dialog.getDialogPane().getButtonTypes().addAll(aBut, ButtonType.CANCEL);
 
	// Create the animalName and password labels and fields.
	GridPane grid = new GridPane();
	grid.setHgap(10);
	grid.setVgap(10);
	grid.setPadding(new Insets(20, 150, 10, 10));
 
	TextField animalName = new TextField();
	animalName.setPromptText("AnimalName");
	TextField img = new TextField();
	img.setPromptText("ImageLink");
	TextField wiki = new TextField();
	wiki.setPromptText("WikiLink");
	TextField youtube = new TextField();
	youtube.setPromptText("YoutubeLink");
 
	grid.add(new Label("AnimalName:"), 0, 0);
	grid.add(animalName, 1, 0);
	grid.add(new Label("WikiLink:"), 0, 1);
	grid.add(img, 1, 1);
	grid.add(new Label("ImageLink:"), 0, 2);
	grid.add(wiki, 1, 2);
	grid.add(new Label("YoutubeLink:"), 0, 3);
	grid.add(youtube, 1, 3);
	...
	return list;
  }
  ...
}
AnimalController manages a list of animals. Each entry consists of a Name, a Link to Wikipedia, a Link to a Web-image and a Link to a video clip (e.g. Youtube). Any modification (add/del) will be saved when Zoo-App terminates (see method panicShutdown). And the ViewModel
PHP:
<SplitPane fx:id="animal" xmlns="http://javafx.com/javafx/8"
	xmlns:fx="http://javafx.com/fxml/1" fx:controller="AnimalController"
	dividerPositions="0.233" prefHeight="180.0" prefWidth="200.0"
	AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
	AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
	<items> 
	  <VBox spacing="5" alignment="bottom_center" >
		 <padding><Insets  right="5" left="5" bottom="5" /></padding>
		 <children>
			<ListView fx:id="animalList" onMouseClicked="#onMouseClicked" />
			<HBox spacing="5" alignment="bottom_center" >
			  <Button fx:id="ADD" onAction="#addAnimal" text="ADD"
				  prefHeight="20.0" prefWidth="80.0" />
			  <Button fx:id="DEL" onAction="#delAnimal" text="DEL"
				  prefHeight="20.0" prefWidth="80.0" />
			  </HBox>
		   </children>
		</VBox>
	   <Pane fx:id="infoPane" minHeight="0.0" minWidth="0.0"
			prefHeight="428.0" prefWidth="511.0" >
	   </Pane>						 
	</items>
</SplitPane>
The other Tabs are;
PHP:
public class InfoController {
	@FXML private Pane infoPane;
 
	private WebEngine wNgine;
 
	@FXML public void initialize() {
		WebView vView = new WebView();
		wNgine = wView.getEngine();
		infoPane.getChildren().add(wView);
	}
	public void setInfo(String url) {
		wNgine.load(url);
	}
}
PHP:
<Pane fx:id="infoPane"
	  xmlns="http://javafx.com/javafx/8"
	  xmlns:fx="http://javafx.com/fxml/1"
	  fx:controller="InfoController"
/>
PHP:
public class VideoController {
	@FXML private Pane videoPane;
 
	private WebEngine vNgine;
 
	@FXML public void initialize() {
		WebView vView = new WebView();
		vNgine = vView.getEngine();
		videoPane.getChildren().add(vView);
	}
 
	public void setVideo(String url) {
		vNgine.load(url);
	}
}
PHP:
<Pane fx:id="videoPane"
	  xmlns="http://javafx.com/javafx/8"
	  xmlns:fx="http://javafx.com/fxml/1"
	  fx:controller="VideoController"
/>
The complete sources can be downloaded hereunder.
 

Attachments

Sửa lần cuối:
  • Like
Reactions: Thanhpv