現在的位置: 首頁 > 綜合 > 正文

SwingUtilities.invokeLater()方法

2018年04月25日 ⁄ 綜合 ⁄ 共 8033字 ⁄ 字型大小 評論關閉

可以解決組件顯示不出來的問題!!!

內容如下:

現在我們要做一個簡單的界面。

包括一個進度條、一個輸入框、開始和停止按鈕。

需要實現的功能是:

當點擊開始按鈕,則更新進度條,並且在輸入框內把完成的百分比輸出(這裡只做例子,沒有真正去做某個工作)

代碼1:

import java.awt.FlowLayout;   

import java.awt.event.ActionEvent;   

import java.awt.event.ActionListener;   

import javax.swing.JButton;   

import javax.swing.JFrame;   

import javax.swing.JProgressBar;   

import javax.swing.JTextField;   

public class SwingThreadTest1 extends JFrame {   

    private static final long serialVersionUID = 1L;   

    private static final String STR = "Completed : ";   

    private JProgressBar progressBar = new JProgressBar();   

    private JTextField text = new JTextField(10);   

    private JButton start = new JButton("Start");   

    private JButton end = new JButton("End");   

    private boolean flag = false;   

    private int count = 0;   

    public SwingThreadTest1() {   

        this.setLayout(new FlowLayout());   

        add(progressBar);   

        text.setEditable(false);   

        add(text);   

        add(start);   

        add(end);   

        start.addActionListener(new Start());   

        end.addActionListener(new End());   

    }   

           

    private void go() {   

        while (count < 100) {   

            try {   

                Thread.sleep(100);//這裡比作要完成的某個耗時的工作   

            } catch (InterruptedException e) {   

                e.printStackTrace();   

            }   

                         //更新進度條和輸入框   

            if (flag) {   

                count++;   

                progressBar.setValue(count);   

                text.setText(STR + String.valueOf(count) + "%");   

            }   

        }   

    }   

    private class Start implements ActionListener {   

        public void actionPerformed(ActionEvent e) {   

            flag = true;//設置開始更新的標誌   

            go();//開始工作   

        }   

    }   

    private class End implements ActionListener {   

        public void actionPerformed(ActionEvent e) {   

            flag = false;//停止   

        }   

    }   

    public static void main(String[] args) {   

        SwingThreadTest1 fg = new SwingThreadTest1();   

        fg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   

        fg.setSize(300, 100);   

        fg.setVisible(true);   

    }   

}

運行代碼發現,

現象1:當點擊了開始按鈕,畫面就卡住了。按鈕不能點擊,進度條沒有被更新,輸入框上也沒有任何信息。

原因分析:Swing是線程不安全的,是單線程的設計,所以只能從事件派發線程訪問將要在屏幕上繪製的Swing組件。ActionListeneractionPerformed方法是在事件派發線程中調用執行的,而點擊了開始按鈕後,執行了go()方法,在go()里,雖然也去執行了更新組件的方法

progressBar.setValue(count);

text.setText(STR + String.valueOf(count) + "%");

但由於go()方法直到循環結束,它並沒有返回,所以更新組件的操作一直沒有被執行,這就造成了畫面卡住的現象。

現象2:過了一段時間(go方法里的循環結束了)後,畫面又可以操作,並且進度條被更新,輸入框也出現了我們想看到的信息。

原因分析:通過在現象1的分析,很容易聯想到,當go()方法返回了,則其他的線程(更新組件)可以被派發了,所以畫面上的組件被更新了。

為了讓畫面不會卡住,我們來修改代碼,將耗時的工作放在一個線程里去做。

代碼2

import java.awt.FlowLayout;   

import java.awt.event.ActionEvent;   

import java.awt.event.ActionListener;   

import javax.swing.JButton;   

import javax.swing.JFrame;   

import javax.swing.JProgressBar;   

import javax.swing.JTextField;   

public class SwingThreadTest2 extends JFrame {   

private static final long serialVersionUID = 1L;   

    private static final String STR = "Completed : ";   

    private JProgressBar progressBar = new JProgressBar();   

    private JTextField text = new JTextField(10);   

    private JButton start = new JButton("Start");   

    private JButton end = new JButton("End");   

    private boolean flag = false;   

    private int count = 0;   

       

    GoThread t = null;   

    public SwingThreadTest2() {   

        this.setLayout(new FlowLayout());   

        add(progressBar);   

        text.setEditable(false);   

        add(text);   

        add(start);   

        add(end);   

        start.addActionListener(new Start());   

        end.addActionListener(new End());   

    }   

    private void go() {   

        while (count < 100) {   

            try {   

                Thread.sleep(100);   

            } catch (InterruptedException e) {   

                e.printStackTrace();   

            }   

            if (flag) {   

                count++;   

                System.out.println(count);   

                progressBar.setValue(count);   

                text.setText(STR + String.valueOf(count) + "%");   

            }   

        }   

    }   

    private class Start implements ActionListener {   

        public void actionPerformed(ActionEvent e) {   

            flag = true;   

            if(t == null){   

                t = new GoThread();   

                t.start();   

            }   

        }   

    }   

    //執行複雜工作,然後更新組件的線程   

    class GoThread extends Thread{   

        public void run() {   

            //do something   

            go();   

        }   

    }   

    private class End implements ActionListener {   

        public void actionPerformed(ActionEvent e) {   

            flag = false;   

        }   

    }   

    public static void main(String[] args) {   

        SwingThreadTest2 fg = new SwingThreadTest2();   

        fg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   

        fg.setSize(300, 100);   

        fg.setVisible(true);   

    }   

}

我們執行了程序,結果和我們想要的一樣,畫面不會卡住了。

那這個程序是否沒有問題了呢?

我們自定義了一個線程GoThread,在這裡我們完成了那些耗時的工作,可以看作是「工作線程」,

而對於組件的更新,我們也放在了「工作線程」里完成了。

在這裡,在事件派發線程以外的線程里設置進度條,是一個危險的操作,運行是不正常的。(對於輸入框組件的更新是安全的。)

只有從事件派發線程才能更新組件,根據這個原則,我們來修改我們現有代碼。

import java.awt.FlowLayout;   

import java.awt.event.ActionEvent;   

import java.awt.event.ActionListener;   

import javax.swing.JButton;   

import javax.swing.JFrame;   

import javax.swing.JProgressBar;   

import javax.swing.JTextField;   

import javax.swing.SwingUtilities;   

public class SwingThreadTest3 extends JFrame {   

    private static final long serialVersionUID = 1L;   

    private static final String STR = "Completed : ";   

    private JProgressBar progressBar = new JProgressBar();   

    private JTextField text = new JTextField(10);   

    private JButton start = new JButton("Start");   

    private JButton end = new JButton("End");   

    private boolean flag = false;   

    private int count = 0;   

       

    private GoThread t = null;   

       

    private Runnable run = null;//更新組件的線程   

    public SwingThreadTest3() {   

        this.setLayout(new FlowLayout());   

        add(progressBar);   

        text.setEditable(false);   

        add(text);   

        add(start);   

        add(end);   

        start.addActionListener(new Start());   

        end.addActionListener(new End());   

           

        run = new Runnable(){//實例化更新組件的線程   

            public void run() {   

                progressBar.setValue(count);   

                text.setText(STR + String.valueOf(count) + "%");   

            }   

        };   

    }   

    private void go() {   

        while (count < 100) {   

            try {   

                Thread.sleep(100);   

            } catch (InterruptedException e) {   

                e.printStackTrace();   

            }   

            if (flag) {   

                count++;   

                SwingUtilities.invokeLater(run);//將對象排到事件派發線程的隊列中   

            }   

        }   

    }   

    private class Start implements ActionListener {   

        public void actionPerformed(ActionEvent e) {   

            flag = true;   

            if(t == null){   

                t = new GoThread();   

                t.start();   

            }   

        }   

    }   

       

    class GoThread extends Thread{   

        public void run() {   

            //do something   

            go();   

        }   

    }   

    private class End implements ActionListener {   

        public void actionPerformed(ActionEvent e) {   

            flag = false;   

        }   

    }   

    public static void main(String[] args) {   

        SwingThreadTest3 fg = new SwingThreadTest3();   

        fg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   

        fg.setSize(300, 100);   

        fg.setVisible(true);   

    }   

}

解釋:SwingUtilities.invokeLater()方法使事件派發線程上的可運行對象排隊。當可運行對象排在事件派發隊列的隊首時,就調用其run方法。其效果是允許事件派發線程調用另一個線程中的任意一個代碼塊。

還有一個方法SwingUtilities.invokeAndWait()方法,它也可以使事件派發線程上的可運行對象排隊。

他們的不同之處在於:SwingUtilities.invokeLater()在把可運行的對象放入隊列後就返回,而SwingUtilities.invokeAndWait()一直等待知道已啟動了可運行的run方法才返回。如果一個操作在另外一個操作執行之前必須從一個組件獲得信息,則應使用SwingUtilities.invokeAndWait()方法。

----2009年02月16日

抱歉!評論已關閉.