Jfx View: Model View Controller With S W I N G And J F X - Part 4 -

Joe

Thành viên VIP
21/1/13
2,938
1,305
113
(Continue of Model View Controller With S W I N G And J F X - Part 3 - )

Before I go into details with JFX let me summarize the parallels between SWING-MVC and JFX.
PHP:
Structural view
+-----------------+---------------------------+----------------------------+
!				 !		  SWING			!		   JFX			  !
+-----------------+---------------------------+----------------------------+
!  Model		  ! plain text with XML	   ! FXML bases on XML		  !
!				 ! syntax (case sensitive)   !							!
+-----------------+---------------------------+----------------------------+
!  View		   ! conventional JAVA		 ! as Extension of the		!
!				 !						   ! Application API			!
+-----------------+---------------------------+----------------------------+
!  Controller	 ! instantiated by View	  ! instantiated by FMXLLoader !
+-----------------+---------------------------+----------------------------+
 
Technical view
+-----------------+---------------------------+----------------------------+
!				 !		  SWING			!		   JFX			  !
+-----------------+---------------------------+----------------------------+
!  Model		  ! shortened J-Components as ! JFX components in		  !
!				 ! keywords:Label for JLabel ! javafx package			 !
!				 ! etc.					  !							!
+-----------------+---------------------------+----------------------------+
!  View		   ! run ModelLoader to load   ! run FXMLLoader to load the !
!				 ! model and to instantiate  ! model AND the controler	!
!				 ! the controller			!							!
+-----------------+---------------------------+----------------------------+
!  Controller	 ! use HashMap to access and ! use @FXML annotation to	!
!				 ! to refer SWING components ! access & refer JFX comp.   !
+-----------------+---------------------------+----------------------------+
JFX Dependencies must be named in FXML model (e.g. onAction="#click") and implemented
in the controller (see example). The interchanges happen with @FXML annotation.
Joe SWING is free from such annotation. The dependencies are "directly" implemented
without having to be named explicitly.
 
Example
+------------+-----------------------------------+---------------------------------+
!			!		  SWING					!		   JFX				   !
+------------+-----------------------------------+---------------------------------+
! Model	  ! <button>name=...........</button> ! <Button id:................. /> !
+------------+-----------------------------------+---------------------------------+
! View	   ! ModelLoader m=new ModelLoader(..) ! FXMLLoader f=new FXMLLoader(..) !
!			! JFrame jf=m.load()				! AnchorPane ap=f.load()		  !
+------------+-----------------------------------+---------------------------------+
! Controller ! public Controller(HashMap<String, ! @FXML Button but;			   !
!			!   Object> map) throws Exception { ! ...							 !
!			!	...//get components			! @FXML // dependency			 !
!			!   JButton b=(JButton)map.get(..); ! private void click() {		  !
!			!   b.addActionListener(e->{		!   ...						   !
!			!		...						! }							   !
!			!   });							 ! ...							 !
!			!   ...							 !								 !
+------------+-----------------------------------+---------------------------------+
The technical view gives you the fundamental differences between self-implemented SWING MVC and Java FX MVC: the Dependency Injection and the instantiation of Controller.
- JFX model and controller go in sync with an arrangement: the dependency must be known by the model (e.g. here: onAction="#click") and implemented by controller (e.g. here: private void click()). This tightly clutched arrangement makes the MVC rigid. Any correction or improvement must be done in the model and the controller.
- I've removed this "rigidity" and let the controller the freedom independently to implement the dependency. Any change, for example actionListener to mouseListener, can be solely done in the controller -the model won't be changed.
- FXMLLoader needs to know the "who's the controller" with the hint fx:controller="MyController" so that it could locate and instantiate it. And that causes an "impact" between model and controller: both must always go "together".
- I move the instantiation out of the model so that the controller can be instantiated anywhere, but before the model is presented (showed) by "setVisible(true)". The "auto-instantiation" of FXMLLoader way can be done with the 2nd constructor of ModelLoader, too (see Part 3).
PHP:
   // load model myModel.txt and the controller MyController
   ModelLoader ml = new ModelLoader(myModel.txt, "MyController");
   JFrame jf = ml.load();
The JFX Structure

(source: tutorialspoint.com)

JFX structure shows 3 distinct layers which resemble a "worldly theater":

- stage
- scene
- the show inside the scene

The JFX View
Unusually compared to "unJFX" java apps JFX app can be started either by the conventional way with the main() method, or directly with the start(). Example:

Direct
PHP:
public class MyAppL extends Application {
  public void start(Stage stage) { // Stage is by convention the parameter
	FXMLLoader fxml = new FXMLLoader(getClass().getResource("myFXML.fxml"));
	AnchorPane root = fxml.load();
	...
	stage.setScene(new Scene(root));
	stage.show();
  }
}
Indirect
PHP:
public class MyAppL extends Application {
  public static void main(String... args){
	launch(args); // by convention
  }
  //
  public void start(Stage stage) { // Stage is by convention the parameter
	FXMLLoader fxml = new FXMLLoader(getClass().getResource("myFXML.fxml"));
	AnchorPane root = fxml.load();
	...
	stage.setScene(new Scene(root));
	stage.show();
  }
}
Some may say the "conventional way" with main() is safer, but it isn't proven that the first ways is unsafe. Probably the old habit is unconquerable. You may notice that the launch() method is started to "launch" the app. It's the convention and you have to "launch" the launch()

JFX application is also driven by the method start(). If there's a "start" it must exist somewhere a stop(), too. Yes, it does exist. The JFX stop() method is always executed when JFX application terminates. Experienced JFX developers usually overwrite this method so that they can do the "clean-up" properly (e.g. DB closing/recovering, etc.) Example:
PHP:
public class MyAppL extends Application {
  public void start(Stage stage) { // Stage is by convention the parameter
	FXMLLoader fxml = new FXMLLoader(getClass().getResource("myFXML.fxml"));
	AnchorPane root = fxml.load();
	...
	stage.setScene(new Scene(root));
	stage.show();
  }
  ...
  public void stop() {
	// do the clean-up action
	Platform.exit(); // properly exit
  }
}
External parameters which are in many case packaged in a String array (String[] args) and by convention it is passed to the main( ) method (it's the C heritage). How does it work with JFX? You can do that in two ways:

1) using a global intermediator:
PHP:
public class MyAppL extends Application {
  public static void main(String... args){
	parms = args; // assign args to parms
	launch(parms); // by convention
  }
  // global parameters
  private static String[] parms;
  //
  public void start(Stage stage) { // Stage is by convention the parameter
	// parms[0] is the given fxml file name
	String fxml = parms.length > 0? parms[0]:"myFXML.fxml";
	FXMLLoader fxml = new FXMLLoader(getClass().getResource(fxml));
	AnchorPane root = fxml.load();
	...
	stage.setScene(new Scene(root));
	stage.show();
  }
  ...
}
2) by JFX convention using the List<String> technique
PHP:
public class MyAppL extends Application {
  public void start(Stage stage) {
	// this is the convention how external variables are passed to JFX app
	List<String> list = getParameters().getRaw();
	// the 1st element of List<String> is the fxml file name
	String fxml = list.size() > 0? list.get(0):"myFXML.fxml";
	FXMLLoader fxml = new FXMLLoader(getClass().getResource(fxml));
	AnchorPane root = fxml.load();
	...
	stage.setScene(new Scene(root));
	stage.show();
  }
  ...
}
Again, I prefer the 2nd way with the "List<String>". With the methods setX() and setY() for the Stage the view can be positioned anywhere on the screen. Example
PHP:
	stage.setX(200);  // 200 on X axis relating to the upper left corner of the screen
	stage.setY(200);  // 200 on Y axis relating to the upper left corner of the screen
	stage.show();	 // presenting the View
(NEXT: the FXML Model)
 
Sửa lần cuối: