<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    風行天下

    JAVA太極
    posts - 4, comments - 10, trackbacks - 0, articles - 55
      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    用Swing編寫反應靈敏的圖形用戶界面

    作者:徐皓

    作者簡介

    徐皓,北京航空航天大學計算機系本科生,你可以通過ertri@163.com與他聯(lián)系。

    正文

    不靈敏的圖形用戶界面會降低應用程序的可用性。當以下現(xiàn)象出現(xiàn)的時候,我們通常說這個用戶界面反應不靈敏。

    1. 不響應事件的現(xiàn)象;
    2. 沒有更新的現(xiàn)象;

    這些現(xiàn)象在很大程度上與事件的處理方法相關,而在編寫Swing應用程序的時候,我們幾乎必然要編寫方法去響應鼠標點擊按鈕,鍵盤回車等事件。在這些方法中我們要編寫一些代碼,在運行時去觸發(fā)一些動作。常見動作包括查找,更新數(shù)據(jù)庫等。在這篇文章中通過對一個實例的分析,介紹了一些基本概念,常見的錯誤以及提出了一個解決方案。

    event-dispatching thread

    我們一定要記住,事件響應方法的代碼都是在event-dispatching thread中執(zhí)行的,除非你啟用另一個線程。

    那么,什么是event-dispatching thread呢?在《Java Tutorial》[1]中,作者給出了一條單一線程規(guī)則:一旦一個Swing組件被實現(xiàn)(realized),所有的有可能影響或依賴于這個組件的狀態(tài)的代碼都應該在event-dispatching thread中被執(zhí)行。而實現(xiàn)一個組件有兩種方式:

    1. 對頂層組件調(diào)用show(), pack(), 或者setVisible(true);
    2. 將一個組件加到一個已經(jīng)被實現(xiàn)的容器中。

    單一線程規(guī)則的根源是由于Swing組件庫的大部分方法是對多線程不安全的,盡管存在一些例外。這些例外的情況可以在《Java Tutorial》[1]的相關章節(jié)找到,這里不再展開。

    為了支持單一線程模型,Swing組件庫提供了一個專門來完成這些與Swing組件相關的操作的線程,而這一線程就是event-dispatching thread。我們的事件響應方法通常都是由這一線程調(diào)用的,除非你自己編寫代碼來調(diào)用這些事件響應方法。在這里初學者經(jīng)常犯的一個錯誤就是在事件響應方法中完成過多的與修改組件沒有直接聯(lián)系的代碼。其最有可能的效果就是導致組件反應緩慢。比如以下響應按鈕事件的代碼:

    String str = null;
    this.textArea.setText("Please wait...");
    try {
        //do something that is really time consuming
        str = "Hello, world!";
        Thread.sleep(1000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    this.textArea.setText(str);	
    

    執(zhí)行之后的效果就是按鈕似乎定住了一段時間,直到Done.出現(xiàn)之后才彈起來。原因就是Swing組件的更新和事件的響應都是在event-dispatching thread中完成的,而事件響應的時候,event-dispatching thread被事件響應方法占據(jù),所以組件不會被更新。而直到事件響應方法退出時才有可能去更新Swing組件。

    為了解決這個問題,有人也許會試圖通過調(diào)用repaint()方法來更新組件:

    final String[] str = new String[1];
    this.jTextArea1.setText("Please wait...");
    this.repaint();
            
    try {
        Thread.sleep(1000L);
    }catch(InterruptedException e) {
        e.printStackTrace();
    }
    str[0] = "Done.";
    
    jTextArea1.setText(str[0]);
    

    但是這一個方法沒有起到預期的作用,按鈕仍然定住一段時間,在察看了repaint()方法的源代碼之后就知道原因了。

    PaintEvent e = new PaintEvent(this, PaintEvent.UPDATE,
        new Rectangle(x, y, width, height));
    Toolkit.getEventQueue().postEvent(e);		
    

    repaint()方法實際上是在事件隊列里加了一個UPDATE的事件,而沒有直接去重畫組件,而且這一個事件只能等待當前的事件響應方法結(jié)束之后才能被分配。因此只有繞過分配機制直接調(diào)用paint方法才能達到目的。

    final String[] str = new String[1];
    this.jTextArea1.setText("Please wait...");
    this.paint(this.getGraphics());
            
    try {
        Thread.sleep(1000L);
    }catch(InterruptedException e) {
        e.printStackTrace();
    }
    str[0] = "Done.";
    
    jTextArea1.setText(str[0]);
    

    這樣卻是實現(xiàn)了更新,但是還存在著以下的問題。雖然從感覺上,按鈕已經(jīng)彈起來了,但是在Done.出現(xiàn)之前,我們卻無法按下這個按鈕??梢哉f按鈕還是定住了,只不過定在了彈起的狀態(tài)。調(diào)用重繪方法無法從根本上解決問題,因此我們需要尋求其他的方法。

    使用多線程

    有效的解決方法是使用多線程。首先看一看一個更好的解決方案,這一方案是在參考《Rethinking Swing Threading》[3]的一個程序片段完成的:

    final String[] str = new String[1];
    this.jTextArea1.setText("Please wait...");
    this.repaint();
            
    new Thread() {
        public void run() {
            try {
                Thread.sleep(1000L);
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
            str[0] = "Done.";
            javax.swing.SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    jTextArea1.setText(str[0]);
                 }
            });
        }
    }.start();
    

    在這個程序中,要花費大量時間的操作被放到另一個線程當中,從而使事件響應方法能快速返回,event-dispatching thread就可以更新UI和響應其它事件了。注意到這個程序使用了invokeLater()方法。invokeLater()方法的作用是讓event-dispatching thread去運行制定的代碼。當然也可以不使用invokeLater()方法,但是這樣就違背了單一線程原則,同時帶來了一定程度的相對多線程的不安全性。到現(xiàn)在,解決方案似乎是完美的了,但是我們看一看在原來的程序添加下面的代碼,盡管我們通常不這樣做。

    public void paint(java.awt.Graphics g) {
        super.paint(g);
        g.drawRect(1, 1, 100, 100);
    }
    

    我們會發(fā)現(xiàn)以前畫的矩形被覆蓋了一部分,原因是由于我們沒用重畫這一個矩形,因此在結(jié)尾加上對repaint()方法的調(diào)用。

    final String[] str = new String[1];
    this.jTextArea1.setText("Please wait...");
    this.repaint();
            
    new Thread() {
        public void run() {
            try {
                Thread.sleep(1000L);
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
            str[0] = "Done.";
            javax.swing.SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    jTextArea1.setText(str[0]);
                    repaint();
                 }
            });
        }
    }.start();
    

    如果你認為這段代碼過于缺乏可讀性,通過在《Java Tutorial》[1]里面介紹的SwingWorker來簡化編程的方法??梢酝ㄟ^實現(xiàn)一個construct()方法來實現(xiàn)花費大量時間的操作和重寫finished()方法來完成組件更新的工作。

    this.jTextArea1.setText("Please wait...");
            
    final SwingWorker worker = new SwingWorker() {
        public Object construct() {
            try {
                Thread.sleep(1000L);
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
            return "Done.";
        }
        public void finished() {
            jTextArea1.setText(getValue().toString());
            repaint();
        }
    };
    worker.start();
    

    在《Rethinking Swing Threading》[3],作者將以上的編程方式稱為同步方式。另外作者提出了一個通過消息機制來實現(xiàn)相同功能的更清晰,但是需要編寫更多代碼的"異步"的方法。

    結(jié)論

    總之,我們在編寫使用Swing組件的程序是要記住以下幾點:

    1、不要過多地占用event-dispatching thread;

    2、與更新組件相關的代碼要使用event-dispatching thread去執(zhí)行;

    3、要更新組件。

    編寫反應靈敏的圖形用戶界面還需要考慮很多問題,以上只是最基本的一部分。歡迎有興趣的讀者來信進行討論。

    參考資料

    [1]Sun Microsystems, The Java Tutorial Third Edition, java.sun.com

    [2]Sun Microsystems, JavaTM 2 SDK, Standard Edition Documentation Version 1.4.2, java.sun.com

    [3]Jonathan Simon, Rethinking Swing Threading, java.net


    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導航:
     
    主站蜘蛛池模板: 国产福利视精品永久免费| 8x网站免费入口在线观看| 日韩在线看片免费人成视频播放| 久久精品亚洲视频| 在线观看肉片AV网站免费| 亚洲精品无码久久久久去q | 永久免费精品影视网站| 亚洲日韩中文在线精品第一| 日本视频免费观看| 亚洲热线99精品视频| 男人进去女人爽免费视频国产| 久久亚洲精品成人777大小说| 一级毛片免费不卡在线| 亚洲视频一区在线观看| 99久久国产热无码精品免费| 亚洲sss综合天堂久久久| 暖暖免费高清日本一区二区三区| 久久亚洲精品无码av| 亚洲国产成人久久一区久久| a级毛片免费观看视频| 7777久久亚洲中文字幕蜜桃 | 亚洲女人影院想要爱| 在线观看免费宅男视频| 日韩在线观看视频免费| 亚洲色偷拍另类无码专区| 久久久久免费看成人影片| 亚洲jjzzjjzz在线观看| 四虎永久成人免费| 97在线免费观看视频| 91亚洲国产成人久久精品网址| 国产自产拍精品视频免费看| 国产免费播放一区二区| 亚洲成人午夜电影| 免费一级成人毛片| 久久精品电影免费动漫| 亚洲成年网站在线观看| 亚洲伊人久久成综合人影院| 国产精品入口麻豆免费观看| 视频一区二区三区免费观看| 亚洲a一级免费视频| 日本免费网站观看|