Toggle Theme Editor
Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate Charcoal

Java Graphics - Jfx Or Swing?

Discussion in 'Java Update' started by Joe, 25/3/19.

  1. Joe

    Joe Thành viên VIP

    JAVA Graphics

    WYSIWYG -What You See Is What You Get- the incisive dogma of the Apple rise in the 1980s. Apple McIntosh and the later Windows 95 were the WYSIWYG-pioneers. Today, an app without icons is a dead app. Unmarketable. ANDROID is popular because of its gestic features in combination with the "a-picture-is-more-than-thousand-words" icons. Computer for the analphabets and the laymen? I don't know. But I am sure that the democratizing of computer demands less knowledge from the users. From the developers, too? Again, I don't know.

    SWING and later JavaFX are the WYSIWYG of JAVA. The ultimate GUI of modern JAVA application development. Yesterday a working student approached me and seeked for advice about the "Flickering and Freezing problems" of JAVA Canvas.

    SWING Canvas and JavaFX Canvas. I am not going to tell you how to compare an apple to a pear, but I only ask you one question:

    If the question is answered by an "a lot", then could you tell my working student or the readers of HERE what is wrong with the repaint() / draw(graph) within the following WHILE-loop:

    SWING
    PHP:
    public class SysMonSWING extends Canvas implements Runnable {
      public 
    SysMonSWING(int widthint height) {
        
    this.width width;
        
    this.height height;
        ...
        
    setPreferredSize(new Dimension(widthheight));
      }
      public 
    void run() {
        try {
          ... 
          while (
    true) {
            ...
            
    repaint( ); // <<< this repaint()
            
    java.util.concurrent.TimeUnit.MILLISECONDS.sleep(150);
            ...
          }
        } catch (
    Exception e) { }
      }
      
    // Override paint()
      
    public void paint(Graphics g) {
        
    g.setColor(Color.black);
        
    g.fillRect(00widthheight);
        
    g.setColor(Color.white);
        
    g.setFont(new Font("veranda"Font.BOLD,12));
        
    g.drawString("PROXY ACTIVITIES MONITORING"13515);
        ...
      }
      ...
    }
    JavaFX (JFX)
    PHP:
    // Joe Nartca (C)
    public class SysMonJFX extends Canvas implements Runnable {
      public 
    SysMonJFX(double widthdouble height) {
        
    super(widthheight);
        
    this.width  width;
        
    this.height height;
        ...
      }
      public 
    void run() {
        try {
          
    GraphicsContext graph getGraphicsContext2D();
          ...
          while (
    true) {
            ...
            
    draw(graph); // this draw()
            
    java.util.concurrent.TimeUnit.MILLISECONDS.sleep(150);
            ...
          }
        } catch (
    Exception e) { }
      }
      private 
    void draw(GraphicsContext g) {
        
    g.setLineWidth(1.4);
        
    g.setFill(Color.BLACK);
        
    g.fillRect(00widthheight);
        
    g.setStroke(Color.ALICEBLUE);
        
    g.strokeText("PROXY ACTIVITIES MONITORING"13515);
        ...
      }
      ...
    }
    The codes are identical -except the two mentioned methods (paint and draw.) Both work exactly the same. The one works with SWING, the other with JavaFX. Both monitor the Computer Activities (CPU load, Memory Usage, etc.)

    [​IMG]

    Well? ...

    Let me tell you. First of all: the codes are fine, BUT they intermitently get some weird effects which are unknown for some people. Even the most omnipotent Stackoverflow presented them with wrong answers or... some work-around solutions.

    An experienced software developer will instantly see the true problem within the codes. It is not the coding error or too much superfluous codes. It is the synchronization problem between the Runnable thread and the SWING (or JFX) thread which runs behind the scene (on behalf of SWING or JFX). And the result of such an imbalanced interoperability between the two threads is the FLICKERING of SWING or the FREEZING of JFX. Very unpleasant. Btw, this flickering problems do happen with JavaScript, too. And the solution could be similar to JAVA (see the Canvas BufferStategy).

    THREADING of SWING and JFX

    Most of JAVA (and other OOPL) learners won't aware about the threading mechanism of JAVA (or other OOPL). Every OOPL bases on the Operating System fundaments (IO, Addressing, Paging/Swapping, etc.) And one of the fundaments is threading. Threads run and work independently from its creator (main app) and that is the understanding problems of OOPL-learners: the synchronization of threads. Probably the newbies just learn the OOPL language before they start to study Computer Science.

    SWING and JFX are dynamic -meaning: lively. They are driven and controlled by threads (and tasks). The following piece of codes shows you how dynamism and liveliness look like and how they work:
    PHP:
    //-------------------- SWING------------------------------
        
    JButton bCook = new JButton((CK"ENABLE":"DISABLE"));
     
        
    bCook.addActionListener(-> { // <---the listening thread
          
    CK = !CK;
          
    bCook.setText((CK"ENABLE":"DISABLE"));
          
    lCook.setText("Cookies is "+(CK"DISABLED":"ENABLED"));
          
    tor.noCookies(CK);
        });
    //-------------------- JavaFX------------------------------
        
    Button bCook = new Button((CK"ENABLE":"DISABLE"));
     
        
    bCook.setOnAction(-> { // <---the listening thread
          
    CK = !CK;
          
    bCook.setText((CK"ENABLE":"DISABLE"));
          
    lCook.setText("Cookies is "+(CK"DISABLED":"ENABLED"));
          
    tor.noCookies(CK);
        });
    addActionListener() and setOnAction() keep their watch dog that lurks in the darkness for every opportunity to bite. The Clicking actions are visible to the developers. But what works behind is invisible to him. It's a triggering thread that must be glued to the button so that it could react on every clicking event. A straightforward algorithm: watch dog waits for clicking. User clicks and watch dog starts to bark.

    But what about the other invisible events? The unseen Events that run independently in background such as image updating, task terminating, etc. There's no user who clicks the button. The watch dogs are the ferocious wild hyenas that can cause some troubles if they are not "controlled" -the synchronization between the independent threads. A frustrating situation like in the introduction codes of CANVAS image updating: flickering or freezing. And if you don't master JAVA (or your favorite OOPL) as a computer engineer -not as a programmer- you are lost. STACKOVERFLOW superficially advices you to switch CANVAS to JPANEL, or JFX CANVAS with ANIMATIONTIMER (or TIMER). Not a word about the cause and an explanation for the switch or for the modification of the codes.

    SWING

    SWING is the starting WYSIWYG of Java. The GUI package javax.swing which bases on the old package java.awt (awt: Abstract Windows Toolkit.) SWING gives you a great possibility to design your fancy GUI apps. Most of the SWING components are associated with one or more active event(s). The events are driven by (anonymous) threads that start to lurk independently when the SWING app becomes "visible" -the setVisible(true) method of JFrame or of JDialog. Before that all events stay inactive (or undefined.)

    SWING Canvas is an AWT object. It provides 3 painting methods: paint(Graphics g), update(Graphics g) and the inherited repaint() of Component. The Canvas refreshing (i.e. updating) happens automaticalliy with the repaint() invocation. The paint() and update() method are usually overriden to suit the individual requirements. As it is, Canvas is NOT glued to any event. Updating is invoked by repaint() which is started by a subprogram in somewhere. Fully unknown and independent to Canvas. The consequence is also: Canvas updating must be somehow synchronized, otherwise it could happen that two or more updates compete against each others and the displaying graphics flickers. And that is the Canvas flickering phenomenon.

    In such a case AWT Canvas should work with different BufferStrategy (see the API) which facilitates the updates to be done on one of the buffers so that no flickering could arise. To do that the above-mentioned SWING codes need at least 2 (buffered) Images as its 2-BufferStrategy. CAUTION: The Image must be created (or instantiated) after the app becomes "visible" (e.g. JFrame.setVisible(true)). Example:

    PHP:
    public class cafeSWING extends JFrame {
        ...
        
    SysMonSWING sysmon = new SysMonSWING(500500);
        
    JPanel jp1 = new JPanel();
        
    jp1.add("Center"sysmon);
     
        
    JTabbedPane tabPane = new JTabbedPane();
        
    tabPane.add(jp1"SystemMonitoring");
        ...
        
    setVisible(true);     // <<<-- display the app: make it visible!
        
    setLocation(3030);
        
    setResizable(false);
        
    // Load 2 JP1 Images to CANVAS only after setVisible(true) is called.
        
    sysmon.setImageBuffer(jp1.createImage(500500), jp1.createImage(500500));
        (new 
    Thread(sysmon)).start();
        ...
    Instead of deriving of the images from JPanel you could directly create 2 BufferedImages as following:
    PHP:
        setVisible(true);
        ...
        
    sysmon.setImageBuffer(new BufferedImage(500500BufferedImage.TYPE_INT_RGB),
                              new 
    BufferedImage(500500BufferedImage.TYPE_INT_RGB));
        (new 
    Thread(sysmon)).start();
    and the modified SysMonSWING could be as following:
    PHP:
    public class SysMonSWING extends Canvas implements Runnable {
      ...
      public 
    void run() {
        try {
          ... 
          while (
    true) {
            ...
            
    preparePaint( ); // <<-- build the graphics in buffer
            
    repaint( );      // <<-- invoke update
            
    java.util.concurrent.TimeUnit.MILLISECONDS.sleep(150);
            ...
          }
        } catch (
    Exception e) { }
      }
      
    // 2 Images as buffers to avoid flickering...
      
    public void setImageBuffer(Image pBuf1Image pBuf2) {
        
    this.pBuf1 pBuf1;
        
    this.pBuf2 pBuf2;
        
    pBuf pBuf1;
      }
      
    // neccessary for a flicker-free presentation
      
    public void update(Graphics g) {
        
    paint(g);
      }
      
    // paint the selected buffer
      
    public void paint(Graphics graphics) {
        
    graphics.drawImage(pBuf00null);
      }
      
    //public void paint(Graphics g) {  <<< unneccessary !
      
    public void preparePaint() {
        
    // switching between the Image Buffers
        
    pBuf = (pBuf == pBuf1)? pBuf2:pBuf1;
        
    Graphics g pBuf.getGraphics();
     
        
    g.setColor(Color.black);
        
    g.fillRect(00widthheight);
        
    g.setColor(Color.white);
        
    g.setFont(new Font("veranda"Font.BOLD,12));
        
    g.drawString("PROXY ACTIVITIES MONITORING"13515);
        ...
      }
      ...
      private 
    Image pBufpBuf1pBuf2;
    repaint() invokes update(), update() calls paint() and paint() draws the graphics from the selected buffer (see preparePaint()). With the 2 alternative Images as buffers the SWING graphics flickering problem is gone.

    The "work-around" solution is to use JPanel to present the graphics. It's indeed simpler, and it could be as following:
    PHP:
    public class SysMonJPANEL extends JPanel implements Runnable {
      ...
      public 
    void run() {
        try {
          ... 
          while (
    true) {
            ...
            
    repaint( );
            
    java.util.concurrent.TimeUnit.MILLISECONDS.sleep(150);
            ...
          }
        } catch (
    Exception e) { }
      }
      
    // Override the paintComponent(), NOT the paint().
      
    protected void paintComponent(Graphics g) {
        
    g.setColor(Color.black);
        
    g.fillRect(00widthheight);
        
    g.setColor(Color.white);
        
    g.setFont(new Font("veranda"Font.BOLD,12));
        
    g.drawString("PROXY ACTIVITIES MONITORING"13515);
        ...
      }
      ...
    and the app
    PHP:
    public class CafeJPANEL extends JFrame {
        ...
        
    SysMonJPANEL sysmon = new SysMonJPANEL(500500);
        (new 
    Thread(sysmon)).start();
     
        
    JTabbedPane tabPane = new JTabbedPane();
        
    tabPane.add(sysmon"SystemMonitoring");
        ...
        
    setVisible(true);
        
    setLocation(3030);
        
    setResizable(false);
        ...
    JavaFX
    JavaFX Canvas is similar to AWT Canvas, but more versatile, more precise and very color-rich. The most striking Graphics features are the positioning and the weight of graphics in double precision (AWT Canvas in integer). And that makes JavaFX graphics smoother, nicer and more fluent than AWT graphics. Similar to AWT is that JavaFX Canvas does have buffering problems, too: instead of flickering it freezes. If a JFX Canvas graphics was too often updated it could come to the unpredictable situation that the graphics seems to be frozen -not flickering, but standing-still.

    Again, the omnipotent Stackoverflow advised the affected people to tinker around: use the other tools such as AnimationTimer or TimeLine for the updates. First: rewriting of the codes, then, well, the animation. It could be fine. But for the precision work such as a measurement of System Activities it is not OK (additional animation activity). Thing is: JavaFX pending events are queued for execution.

    If too much events are simultaneously pending application could become unresponsive. In other words: the app is frozen. To avoid such "simultaneousness" of pending events JavaFX events should be put to an event queue for a "later" execution (i.e. without waiting for completion). And that can be done with the Platform method runLater(). Example with the above mentioned JavaFX Canvas codes:
    PHP:
    // Joe Nartca (C)
    public class SysMonJFX extends Canvas implements Runnable {
      public 
    SysMonJFX(double widthdouble height) {
        
    super(widthheight);
        
    this.width  width;
        
    this.height height;
        ...
      }
      public 
    void run() {
        try {
          
    GraphicsContext graph getGraphicsContext2D();
          ...
          while (
    true) {
            ...
            
    // don't wait for completion. Just run it later!
            
    Platform.runLater(() -> {
              
    draw(graph);
            });
            
    java.util.concurrent.TimeUnit.MILLISECONDS.sleep(150);
            ...
          }
        } catch (
    Exception e) { }
      }
      private 
    void draw(GraphicsContext g) {
        
    g.setLineWidth(1.4);
        
    g.setFill(Color.BLACK);
        
    g.fillRect(00widthheight);
        
    g.setStroke(Color.ALICEBLUE);
        
    g.strokeText("PROXY ACTIVITIES MONITORING"13515);
        ...
      }
      ...
    }
    As you see: no rewriting the codes, no new animation thread. Only ONE additional statement: the runLater() method.

    Interoperability between SWING and JFX

    [​IMG]

    SWING and JavaFX are both useful and have their own advantages and disadvantages. JavaFX came later than SWING and is therefore more modern and more colorful than SWING. However a developer could come to a situation that a complex piece of SWING codes (or JFX codes) should be reused without having totally to convert this piece into JavaFX or vice-versa.

    Difficult? No. Easy? Yes, it is possible to reuse some SWING pieces within JFX application and vice-versa. SWING to JavaFX it is the SwingNode and the opposite is JFXPanel. The usage of SwingNode or JFXPanel is a straight forward matter (details: just study the API description and the given applied examples). In our cases:

    JFX uses SWING codes
    PHP:
    public class CafeJFX_SWING extends Application {
      ...
      public 
    void start(Stage stage) {
        ...
        
    TabPane tabPane = new TabPane();
        
    Tab sysMon = new Tab("SystemMonitoring");
        
    sysMon.setContent(startSysMon( ));
        ...
      }
      ...
      
    // create a SwingNode with SWING codes for JavaFX
      
    private SwingNode startSysMon( ) {
        
    SwingNode sysmon = new SwingNode();
        
    javax.swing.SwingUtilities.invokeLater(() -> {
          
    SysMonJPANEL swing = new SysMonJPANEL(500500);
          (new 
    Thread(swing)).start();
          
    sysmon.setContent(swing);
        });
        return 
    sysmon;
      }
      ...
    You may notice that I run here the SysMonJPANEL instead of the SysMonSWING (with Canvas) and wonder for the reasons. Well, as I discussed above with you about the BufferStrategy and hinted that the buffered images can be only usable after the (JFrame) application is setVisible(true). That is the only reason why SysMonSWING won't work here, but SysMonJPANEL will. And the vice-versa with SWING with JavaFX piece.

    SWING uses JavaFX codes
    PHP:
    public class CafeSWING_JFX extends JFrame {
      ...
      public 
    CafeSWING_JFX(String[] parms) {
        ...
        
    JTabbedPane tabPane = new JTabbedPane();
        
    JPanel jp1 = new JPanel();
        
    jp1.add("Center"jfxPanel( ));
        
    tabPane.add(jp1"SystemMonitoring");
     
      }
      ...
      
    // create a JFXPanel with JavaFX codes for SWING
      
    private JFXPanel jfxPanel( ) {
        
    JFXPanel jfx = new JFXPanel();
        
    Group root = new Group();
        
    SysMonJFX sysmon = new SysMonJFX(500500);
        (new 
    Thread(sysmon)).start();
        
    root.getChildren().add(sysmon);
        
    Scene scene = new Scene(root500500);
        
    jfx.setScene(scene);
        return 
    jfx;
      }
      ...
    Examples
    The simplified sources

    - CafeJFX - Application in pure JavaFX
    - CafeSWING - Application in pure SWING
    - SysMonJFX - the SytemMonitor API in pure JavaJFX
    - SysMonJPANEL - the SytemMonitor API in pure SWING

    can be downloaded HERE. You could try with some mdifications with SwingNode and JFXPanel to see how the interoperability between SWING and JavaFX work.

    NOTE: the file graphics.txt is the JZIPped file. To "unjzip" this file you have to download the source JZIP.java (see the thread Zip or Gnu-zip) and compile it as I showed you in the mentioned thread.

    Have fun ;)
     

    Attached Files:

    Last edited: 25/3/19
    tranhuyvc and badboy3283 like this.
  2. badboy3283

    badboy3283 Active Member

    I'm here by Google, someone told me that Nodejs can make Desktop Application by HTML template and CSS, then he ask me "can Java do it easy?"
    The first thing I remember is SWING, I can't tell him how can I design so hard a SWING application, that why I ask Google "Java Application by CSS" and see this post.
    Welcome back and thanks so much JOE
     
    Joe and javaee like this.
  3. manguon.dev

    manguon.dev New Member

    I LIKE JAFAFX
     
  4. Joe

    Joe Thành viên VIP

    Hi Badboy3283
    NOT as a Mod, but as a member. It's the fumbling work of Boss Tranhuyvc
    Btw, JavaFX with css and its MVC features is superb enough to do whatever you want. Even with SVG (Scalable Vector Graphics)
     

Chia sẻ trang này

Loading...