日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

JAVA Painting-Swing實(shí)現(xiàn)紀(jì)要一 - - JavaEye技術(shù)網(wǎng)站

 xuanda007 2010-12-15
VA Painting-Swing實(shí)現(xiàn)紀(jì)要一
首先推薦<Painting in AWT and Swing>by Amy Fowler。
Sun在JDK 1.0最初發(fā)布了圖形API包,代號(hào)AWT (abstract windowing toolkit),里面除對(duì)GUI基本支持(如結(jié)合各OS的事件分發(fā)機(jī)制等)外,自有一套重量級(jí)開(kāi)發(fā)GUI的思路,并提供了一組常規(guī)使用的重量級(jí)組件。所謂重量級(jí)組件就是每個(gè)組件都引用一個(gè)本地對(duì)等體peer成員對(duì)象,這個(gè)對(duì)等體對(duì)象利用本地系統(tǒng)GUI API繪制組件。后來(lái)在JDK1.1,AWT包中引進(jìn)了一套輕量級(jí)開(kāi)發(fā)GUI的新思路,并提供了一組輕量級(jí)組件。所謂輕量級(jí)組件就是自身沒(méi)有本地對(duì)等體,而借助重量級(jí)組件作為容器來(lái)繪制組件。JDK 1.1之后,sun在開(kāi)發(fā)GUI思路上,在效率,擴(kuò)展性等方面給出了很多創(chuàng)新,并基于這種新思路推出一套豐富的新組件(輕量級(jí)組件),sun為此打出一個(gè)新的響亮的代號(hào)---Swing,并推薦以后的GUI開(kāi)發(fā)都應(yīng)該基于SWING的GUI開(kāi)發(fā)思路開(kāi)展,應(yīng)該使用或擴(kuò)展這套SWING的組件。
不論是AWT模式還是SWING模式,Sun的GUI開(kāi)發(fā)思路都是純OO的。開(kāi)發(fā)人員總是構(gòu)建多個(gè)組件對(duì)象實(shí)例來(lái)組合建立GUI,這些對(duì)象是因不同的輸入輸出表現(xiàn)被封裝為多種組件類(lèi)的實(shí)例,而這些組件類(lèi)是有合理的繼承關(guān)系因而容易擴(kuò)展的“套件”。而且兩種模式最基本的統(tǒng)一的程序運(yùn)行思路都是:
1.通過(guò)建立各種組件的實(shí)例來(lái)負(fù)責(zé)GUI的工作。
2. 約定出GUI變化時(shí)機(jī)—java應(yīng)用程序隨需發(fā)出請(qǐng)求調(diào)用或?qū)Σ僮飨到y(tǒng)級(jí)某種操作的監(jiān)聽(tīng)(如暴露被遮擋的窗口內(nèi)容)。
3. 在時(shí)機(jī)到來(lái)時(shí)由“框架程序”來(lái)判斷并調(diào)用應(yīng)該調(diào)用的目標(biāo)組件實(shí)例所提供的各種形式的paint方法(各組件在此方法里通過(guò)java 2d API包來(lái)實(shí)現(xiàn)自己的具體繪制邏輯)來(lái)完成各組件繪制。
4. 在GUI的整個(gè)生命周期里,通過(guò)以上的123模式來(lái)完成整個(gè)應(yīng)用界面的隨需而變。
下文將主要分析SWING模式。
Swing式 開(kāi)發(fā)GUI的基本約定包括:SWING提供4個(gè)頂層容器JFrame,JDialog,JApplet,JWindow,如果是桌面應(yīng)用,則GUI必須要有一個(gè)JFrame,如果是瀏覽器應(yīng)用,則GUI必須要有一個(gè)JApplet。其他swing組件,或自定義開(kāi)發(fā)的Swing組件都擴(kuò)展自JComponent,并且其實(shí)例要存在于頂層容器的層次樹(shù)中。下面是一個(gè)符合約定的GUI的運(yùn)行分析。
import javax.swing.JFrame;
import javax.swing.JLabel;
public class BasicSwing {
public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
private void createAndShowGUI() {
JFrame frame = new JFrame("BasicSwing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JLabel label=new JLabel("hello world");
        frame.getContentPane().add(label);
        frame.setSize(100,200);
        frame.setVisible(true);
}
        });
    }
}
invokeLater方法在執(zhí)行時(shí)首先會(huì)延遲創(chuàng)建getToolkit---系統(tǒng)屬性awt.toolkit給出了要加載的類(lèi),在windows平臺(tái)下即為WToolkit。WToolkit在初始化時(shí)會(huì)啟動(dòng)AWT-WINDOWS線程(setDaemon-true),該線程將一直負(fù)責(zé)從win32系統(tǒng)中獲取底層事件并簡(jiǎn)接掛到EventQueue事件隊(duì)列中;同時(shí)激活A(yù)WT-Shutdown線程(setDaemon-false),該線程一直監(jiān)測(cè)是否滿足關(guān)閉GUI的條件(peerMap is null;AWT-WINDOWS is busy;EDT is busy),若是則主動(dòng)要求關(guān)閉EDT也就是GUI最終退出(因?yàn)镚UI環(huán)境下只有EDT是非daemon線程);WToolkit還有就是加載sun.java2d.Disposer類(lèi),其將在類(lèi)加載初始化時(shí)啟動(dòng)Java2D Disposer線程(setDaemon-true, MAX_PRIORITY),該線程將一直跟蹤監(jiān)測(cè)被廢棄的注冊(cè)記錄(WToolkit算一個(gè),還有各種peer),監(jiān)測(cè)到后執(zhí)行對(duì)應(yīng)的Dispose類(lèi)來(lái)完成相應(yīng)的資源回收。
invokeLater方法同時(shí)會(huì)創(chuàng)建EventQueue掛在AppContext里,并馬上向EventQueue提交InvocationEvent以執(zhí)行上面例子中的Runable,這將導(dǎo)致啟動(dòng)第一個(gè)AWT-EventQueue-N線程(EDT-(setDaemon-false))。
EDT啟動(dòng)后將一直從EventQueue獲取AWTEVENT進(jìn)行dispatch分發(fā)處理,處理過(guò)程中若遇到某些意外或被強(qiáng)制中斷都有可能導(dǎo)致EDT熄火,此時(shí)AWT-Shutdown被notify檢測(cè)徹底終止AWT的時(shí)機(jī)是否到來(lái),若不滿足條件新的EDT:AWT-EventQueue-N+1將被啟動(dòng)。
以上將建立起界面GUI的基本運(yùn)行框架。上述例子的main線程很快退出,而EDT線程將處理InvocationEvent,處理過(guò)程即是執(zhí)行Runable.run方法體。
在EDT中,JFrame被構(gòu)造,在其構(gòu)造過(guò)程中,會(huì)追加dispose記錄即
(addRecord(JFrame.anchorObject, WindowDisposerRecord(appContext,this));)進(jìn)Java2D Disposer以在失去引用時(shí)釋放窗口資源。
隨后JFrame被setvisible,在setvisible過(guò)程中,將通過(guò)WToolkit createFramePeer,并注冊(cè)在AWT-Shutdown的peerMap中以支持AWT-AutoShutDown機(jī)制。
Setvisible中將促使調(diào)用peer.pShow-native代碼,即發(fā)送給win32請(qǐng)求顯示窗口,窗口被打開(kāi)后awt_windows線程在eventloop中得到wm_paint消息進(jìn)行處理,這是一個(gè)異步過(guò)程。
awt_windows處理中將有選擇地通過(guò)RepaintManager加入重畫(huà)記錄區(qū)幾何區(qū)域
RepaintManager. nativeAddDirtyRegio并調(diào)度重畫(huà)線程單位在EDT中進(jìn)行繪制
postEvent-InvocationEvent(ProcessingRunnable),ProcessingRunnable隨后
在EDT中run時(shí)將根據(jù)重畫(huà)區(qū)記錄執(zhí)行可能的窗口內(nèi)容繪制--即各子組件回調(diào)paint過(guò)程。
上述是SWING頂層重量級(jí)容器組件的一個(gè)繪制場(chǎng)景,可以看到是經(jīng)由awt-windows eventloop到了底層事件后觸發(fā)paint繪制;然而對(duì)輕量級(jí)swing組件,其paint都是通過(guò)java代碼中對(duì)repaint調(diào)用而觸發(fā),其會(huì)向RepaintManager.addDirtyRegion,同時(shí)scheduleProcessingRunnable。這是整個(gè)GUI生命周期內(nèi)對(duì)繪制的兩種不同的觸發(fā)方式,但觸發(fā)后的處理都是交由RepaintManager。
回過(guò)頭去看,JFrame被構(gòu)造的時(shí)候就會(huì)創(chuàng)建root pane, layered pane,content pane, glass pane等,這些沒(méi)有對(duì)等體的輕量級(jí)Swing組件在構(gòu)造時(shí)都將repaint。雖然在創(chuàng)建windows對(duì)等窗口之前這些Swing組件就已經(jīng)在要求繪制,但是RepaintManager能夠協(xié)調(diào)好這個(gè)步調(diào)(具體即是當(dāng)收到repaint請(qǐng)求時(shí)要判斷情況,像這時(shí)的請(qǐng)求因?yàn)轫攲尤萜鬟€沒(méi)有繪制則不會(huì)記錄到重畫(huà)區(qū))。所以最終效果就是在peer.pshow的時(shí)候只能看到一個(gè)空窗口,隨后底層消息到來(lái)后通過(guò)paint回調(diào)畫(huà)這些子組件,最后hello world才顯示出來(lái)。如果眼神好,能夠看出這有一個(gè)“閃爍”。
這是一個(gè)最簡(jiǎn)單的swing應(yīng)用程序的基本運(yùn)行機(jī)制分析,下面再具體分析。
Swing的GUI總是由頂層容器組件和輕量級(jí)swing組件組合建立,頂層容器和其他組件區(qū)別主要在于頂層容器沒(méi)有自身的paint邏輯。
所有頂層容器都是通過(guò)使用底層系統(tǒng)API來(lái)繪制對(duì)等體的方式進(jìn)行paint,自身沒(méi)有java2d的paint邏輯實(shí)現(xiàn),對(duì)等體畫(huà)成什么樣頂層容器就是什么樣,它只是可以控制對(duì)等體的一些可配顯示屬性。所以效果就是比如在windows平臺(tái)上畫(huà)一個(gè)jframe,除在桌面上顯示一個(gè)窗口還會(huì)在任務(wù)欄上顯示一個(gè)條目。Swing的4個(gè)頂層容器都是在addNotify時(shí)才會(huì)getToolkit().createPeer(this)(Frame/Dialog/Window),而addNotify并不是在構(gòu)造時(shí)被調(diào)用,而是在pack/show或setvisible(這3個(gè)所謂的realized具現(xiàn)化方法)時(shí)被調(diào)用。創(chuàng)建了對(duì)等體peer后還要通過(guò)peer.pShow(show/setVisible(true)調(diào)用)調(diào)用才會(huì)要求底層系統(tǒng)進(jìn)行顯示(所以只有pack是不會(huì)顯示窗口的)。在顯示窗口后底層消息隊(duì)列得到通知,此后隨著窗口被最小化后恢復(fù)或被遮蓋后恢復(fù)等系統(tǒng)操作后同樣能從底層消息得到通知,這時(shí)的監(jiān)聽(tīng)處理將有選擇地通知給RepaintManager一個(gè)重畫(huà)請(qǐng)求進(jìn)行窗口內(nèi)容-子組件重畫(huà)。
而輕量級(jí)swing組件將繪制有關(guān)的職責(zé)都委托給了ui成員對(duì)象,ui對(duì)象使用JAVA2D API 進(jìn)行繪制,paint成什么樣那就是這個(gè)組件的樣子。具體就是在構(gòu)造的時(shí)候即要updateUI{setUI(UIManger.getUI(this))}。UIManger會(huì)根據(jù)當(dāng)前L&F的選擇,根據(jù)this.uiClassID來(lái)得到ui成員類(lèi)并建立實(shí)例,以后的paint回調(diào)等都推托給ui成員類(lèi)paint,這也算是一種策略模式。Setui的過(guò)程中除了保存這個(gè)ui實(shí)例外,將repaint來(lái)通知RepaintManager進(jìn)行paint回調(diào)完成組件繪制。輕量級(jí)swing組件在addNotify時(shí)也會(huì)去創(chuàng)建對(duì)等體getToolkit().createPeer(this)( LightWeightPeer),但這個(gè)peer的實(shí)現(xiàn)(NullComponentPeer)是個(gè)空殼子,只是作為一個(gè)輕量級(jí)組件的標(biāo)記,以后的很多事件處理等都要判斷peer是否instance of LightWeightPeer從而能夠進(jìn)行不同處理。同樣的Addnotify也不是在構(gòu)造時(shí)被調(diào)用,而是在被加入container時(shí)被調(diào)用。
注意:構(gòu)造方法本身就是狀態(tài)模式的第一狀態(tài),所以GUI組件的構(gòu)造方法里就應(yīng)該要努力完成自身的繪制來(lái)符合自己的地位。輕量級(jí)組件就是按這個(gè)意義在構(gòu)造方法里去通知repaintmanager進(jìn)行自身繪制的,但是頂層容器卻將真正的繪制意圖createPeer延遲到了具現(xiàn)方法里。這是因?yàn)槭紫纫粋€(gè)合乎思維的表達(dá)邏輯是先有容器,再將子組件向容器里添加, 所以最頂層容器總是先行構(gòu)造出來(lái),然后再被一層層地追加輕量級(jí)子組件。如果最頂層容器在構(gòu)造時(shí)就去具現(xiàn),則就要求后續(xù)的構(gòu)造都應(yīng)該在EDT中進(jìn)行,而且每次add子組件都要導(dǎo)致revalidate;但若將最頂層容器的繪制分離延遲到具現(xiàn)方法里,則可以表達(dá)是在容器里盛滿了要顯示的子組件后再一股腦具現(xiàn)繪制出來(lái)的概念,類(lèi)似于在進(jìn)行一次web頁(yè)面的完整加載,然后注意在具現(xiàn)方法執(zhí)行后如果要操作組件都在EDT中進(jìn)行即可,而且頂層容器提供一個(gè)特有的pack方法,用來(lái)一次性對(duì)所有子組件驗(yàn)證大小位置進(jìn)行重布局,pack之后再show,這樣的一次性計(jì)算展現(xiàn)是最有效率的。
頂層容器和輕量級(jí)組件就是這樣誕生并繪制的,在此后的生命周期里,都將按事件監(jiān)聽(tīng)機(jī)制完成GUI隨需而變,無(wú)論是系統(tǒng)事件,還是因?yàn)閞epaint調(diào)用主動(dòng)post事件,事件到來(lái)后再在EDT中執(zhí)行監(jiān)聽(tīng)器里的paint繪制。Swing已經(jīng)提供的頂層容器和輕量級(jí)組件因各自的定義已經(jīng)注冊(cè)了各自的paint監(jiān)聽(tīng),開(kāi)發(fā)人員可以再行維護(hù)或按此模式開(kāi)發(fā)新組件從而滿足應(yīng)用的需要。比如,jbutton默認(rèn)有mousepress listener,在mousepress事件到來(lái)后,監(jiān)聽(tīng)響應(yīng)中會(huì)設(shè)置鼠標(biāo)顏色加深來(lái)表示按下,然后再調(diào)用repaint要求重畫(huà),隨后在EDT中執(zhí)行jbutton的paint回調(diào),此時(shí)按深顏色繪制,于是一個(gè)被按下的效果就出來(lái)了。
下面在具體分析各類(lèi)事件的處理。
對(duì)于頂層容器的受底層事件消息的觸發(fā),當(dāng)?shù)玫降耐ㄖ且驗(yàn)閑xpose暴露隱西藏(暴露被遮蔽的部分或恢復(fù)最小化或第一次繪制等)時(shí),處理過(guò)程會(huì)涉及到雙緩存的處理,即如果可能,直接使用緩存中的舊圖像信息進(jìn)行覆蓋而不再重新繪制。
所謂雙緩存機(jī)制是將一整片的顯示內(nèi)容暫時(shí)寫(xiě)入一張內(nèi)存空間里,然后一次性內(nèi)存拷入顯示區(qū)來(lái)進(jìn)行顯示,這樣處理是因?yàn)槿绻苯訉?xiě)入顯示區(qū),隨著顯示區(qū)被該寫(xiě)入線程逐漸寫(xiě)入,可能經(jīng)歷多次屏幕刷新,導(dǎo)致每次刷新都形成過(guò)程圖像,給人眼造成閃爍感覺(jué);同時(shí)一個(gè)副收益就是可以針對(duì)每個(gè)窗口都做緩存待用(而不僅僅是針對(duì)一個(gè)屏幕雙緩存),當(dāng)窗口被遮擋的部分重現(xiàn)時(shí)直接拷貝緩存來(lái)覆蓋,不用再執(zhí)行繪畫(huà)邏輯,提高了效率。
現(xiàn)在的OS一般都提供雙緩存機(jī)制支持,如果底層系統(tǒng)自身支持以每個(gè)窗口為單位做雙緩存,則該expose消息將被本地處理,不需要通知進(jìn)行子組件的繪制;如果底層不支持,則該消息會(huì)到達(dá)wcomponetpeer.handleexpose中進(jìn)行回調(diào)處理,此時(shí)swing機(jī)制下有一個(gè)參數(shù)控制的雙緩存機(jī)制可以提供。這里的參數(shù)控制需要從RepaintManager的構(gòu)造過(guò)程說(shuō)起。
首先RepaintManager可以通過(guò)static setCurrentManager(SomeCurrentManager)來(lái)進(jìn)行全局指定。默認(rèn)情況使用currentRepaintManager(){new RepaintManager(BUFFER_STRATEGY_TYPE)}得到一個(gè)延遲創(chuàng)建的單例。RepaintManager有一段靜態(tài)類(lèi)初始化過(guò)程,涉及到雙緩存設(shè)置:
static {
nativeDoubleBuffering = "true".equals(AccessController.doPrivileged(
                    new GetPropertyAction("awt.nativeDoubleBuffering")));//JVM的啟動(dòng)參數(shù)控制,默認(rèn)false
        String bs = AccessController.doPrivileged(
                          new GetPropertyAction("swing.bufferPerWindow"));//是否每窗口緩存。
        if (headless) {
            BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF;
        }
        else if (bs == null) {
            BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_NOT_SPECIFIED;
        }
        else if ("true".equals(bs)) {
            BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_ON;
        }
        else {
            BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF;
        }
}
private RepaintManager(short bufferStrategyType) {
        // If native doublebuffering is being used, do NOT use
        // Swing doublebuffering.
        doubleBufferingEnabled = !nativeDoubleBuffering;

this.bufferStrategyType = bufferStrategyType;

}
public void setDoubleBufferingEnabled(boolean aFlag) {
        doubleBufferingEnabled = aFlag;

doubleBufferingEnabled(開(kāi)啟雙緩存),nativeDoubleBuffering(利用本地雙緩存機(jī)制),bufferStrategyType(每窗口雙緩存策略)
這幾個(gè)參數(shù)將影響到RepaintManager的成員對(duì)象paintManager的選擇,也算是一個(gè)策略模式,該paintManager是負(fù)責(zé)繪制的核心類(lèi)。
private synchronized PaintManager getPaintManager() {
        if (paintManager == null) {
            PaintManager paintManager = null;
            if (doubleBufferingEnabled && !nativeDoubleBuffering) {
                switch (bufferStrategyType) {
                case BUFFER_STRATEGY_NOT_SPECIFIED:
                    if (((SunToolkit)Toolkit.getDefaultToolkit()).
                                                useBufferPerWindow()) {//windows下是否禁用vista dwm,在沒(méi)有聲明bufferPerWindow的情況下由windows系統(tǒng)特性確定paintmanager。
                        paintManager = new BufferStrategyPaintManager();
                    }
                    break;
                case BUFFER_STRATEGY_SPECIFIED_ON:
                    paintManager = new BufferStrategyPaintManager();
                    break;
                default:
                    break;
                }
            }
            // null case handled in setPaintManager
            setPaintManager(paintManager);
        }
        return paintManager;
    }
void setPaintManager(PaintManager paintManager) {
        if (paintManager == null) {
            paintManager = new PaintManager();
        }

}
回到上文,當(dāng)handleexpose時(shí),通過(guò)getPaintEventDispatcher 來(lái)createPaintEvent,在UIManager.initialize根據(jù)RepaintManager.HANDLE_TOP_LEVEL_PAINT(屬性swing.handleTopLevelPaint)確定是SwingPaintEventDispatcher還是直接使用PaintEventDispatcher。
若為false,在PaintEventDispatcher中,將直接創(chuàng)建PaintEvent-PAINT提交,此后該事件經(jīng)合并后將由wcomponentpeer.handleEvent,該處理將通過(guò)一個(gè)自身維護(hù)的paintArea幾何臟區(qū)域進(jìn)行重畫(huà)區(qū)域優(yōu)化,最終委托給Container進(jìn)行子組件繪制,這是非SWING模式-即AWT模式,沒(méi)有雙緩存的概念。
補(bǔ)充:在Swing和它的RepainManager出現(xiàn)以前,GUI的模式-AWT模式總是要先形成一個(gè)PaintEvent(觸發(fā)可能來(lái)自底層消息-PAINT類(lèi)型,也可能來(lái)自repaint-UPDATE類(lèi)型),post給EventQueue,并組織一次合并:
public abstract class WComponentPeer{
void handlePaint(int x, int y, int w, int h) {
    System.out.println("handlePaint>>>"+x+":"+y+":"+w+":"+h);
        postPaintIfNecessary(x, y, w, h);
    }

    private void postPaintIfNecessary(int x, int y, int w, int h) {
        if ( !ComponentAccessor.getIgnoreRepaint( (Component) target) ) {
            PaintEvent event = PaintEventDispatcher.getPaintEventDispatcher().
                createPaintEvent((Component)target, x, y, w, h);
            if (event != null) {
                postEvent(event);
            }
        }
}
public class PaintEventDispatcher {
public PaintEvent createPaintEvent(Component target, int x, int y, int w,
                                       int h) {

        return new PaintEvent((Component)target, PaintEvent.PAINT,
                              new Rectangle(x, y, w, h));
    }

public abstract class Component{
public void repaint(long tm, int x, int y, int width, int height) {
        if (this.peer instanceof LightweightPeer) {
                ~~~
                parent.repaint(tm, px, py, pwidth, pheight);
            }
        } else {
            if (isVisible() && (this.peer != null) &&
                (width > 0) && (height > 0)) {
                PaintEvent e = new PaintEvent(this, PaintEvent.UPDATE,
                                              new Rectangle(x, y, width, height));
                Toolkit.getEventQueue().postEvent(e);
            }
        }
}

public class EventQueue{
private void postEvent(AWTEvent theEvent, int priority) {
        if (coalesceEvent(theEvent, priority)) {//post之前總是需要合并
            return;
        }


private boolean coalesceEvent(AWTEvent e, int priority) {

        if (e instanceof PaintEvent) {
            return coalescePaintEvent((PaintEvent)e);//對(duì)paintevent進(jìn)行一輪合并處理,導(dǎo)致同一重量級(jí)組件的多次paintevent被合并為一個(gè)paintevent等待dispatch。以提高效率
        }

然后EDT中在Component.dispatchImpl中委托給wcomponentpeer處理。

public abstract class Component{

dispatchEventImpl{
/*
         * 9. Allow the peer to process the event.
         * Except KeyEvents,
         */
            if (tpeer != null) {
                tpeer.handleEvent(e);
            }
       

public abstract class WComponentPeer{

public void handleEvent(AWTEvent e) {

switch(id) {
            case PaintEvent.PAINT:
                // Got native painting
                paintPending = false;
                // Fallthrough to next statement
            case PaintEvent.UPDATE:
                // Skip all painting while layouting and all UPDATEs
                // while waiting for native paint
                if (!isLayouting && ! paintPending) {
                    paintArea.paint(target,shouldClearRectBeforePaint());
                }
                return;
            default:
            break;
        }

Peer處理過(guò)程中將利用自身維護(hù)的PaintArea進(jìn)行重畫(huà)區(qū)域的優(yōu)化,并執(zhí)行子組件paint回調(diào)。
/**
     * Invokes paint and update on target Component with optimal
     * rectangular clip region.
     * If PAINT bounding rectangle is less than
     * MAX_BENEFIT_RATIO times the benefit, then the vertical and horizontal unions are
     * painted separately.  Otherwise the entire bounding rectangle is painted.
     *
     * @param   target Component to <code>paint</code> or <code>update</code>
     * @since   1.4
     */
    public void paint(Object target, boolean shouldClearRectBeforePaint) {
        Component comp = (Component)target;
~~~
        if (ra.paintRects[HORIZONTAL] != null && ra.paintRects[VERTICAL] != null) {
            Rectangle paintRect = ra.paintRects[HORIZONTAL].union(ra.paintRects[VERTICAL]);
            int square = paintRect.width * paintRect.height;
            int benefit = square - ra.paintRects[HORIZONTAL].width
                * ra.paintRects[HORIZONTAL].height - ra.paintRects[VERTICAL].width
                * ra.paintRects[VERTICAL].height;
            // if benefit is comparable with bounding box
            if (MAX_BENEFIT_RATIO * benefit < square) {
                ra.paintRects[HORIZONTAL] = paintRect;
                ra.paintRects[VERTICAL] = null;
            }
        }
        for (int i = 0; i < paintRects.length; i++) {
            if (ra.paintRects[i] != null
                && !ra.paintRects[i].isEmpty())
            {
                // Should use separate Graphics for each paint() call,
                // since paint() can change Graphics state for next call.
                Graphics g = comp.getGraphics();
                if (g != null) {
                    try {
                        g.setClip(ra.paintRects[i]);
                        if (i == UPDATE) {
                            updateComponent(comp, g);
                        } else {
                            if (shouldClearRectBeforePaint) {
                                g.clearRect( ra.paintRects[i].x,
                                             ra.paintRects[i].y,
                                             ra.paintRects[i].width,
                                             ra.paintRects[i].height);
                            }
                            paintComponent(comp, g);
                        }
                    } finally {
                        g.dispose();
                    }
                }
            }
        }
    }
若為true,在SwingPaintEventDispatcher.createPaintEvent,
if (component instanceof RootPaneContainer) {//如果是頂層容器
            AppContext appContext = SunToolkit.targetToAppContext(component);
            RepaintManager rm = RepaintManager.currentManager(appContext);
            if (!SHOW_FROM_DOUBLE_BUFFER ||//參數(shù)swing.showFromDoubleBuffer控制,默認(rèn)true確定swing//是否會(huì)考慮雙緩存支持
                  !rm.show((Container)component, x, y, w, h)) {
                rm.nativeAddDirtyRegion(appContext, (Container)component,
                                        x, y, w, h);
            }
return new IgnorePaintEvent(component, PaintEvent.PAINT,
                                        new Rectangle(x, y, w, h));//返回一個(gè)將被忽略的假事件提交

如果SHOW_FROM_DOUBLE_BUFFER 考慮雙緩存支持,將進(jìn)行rm.show,其交給getPaintManager().show,這時(shí)的paintmanager是經(jīng)過(guò)了前面所說(shuō)的幾個(gè)參數(shù)選擇的,也就是說(shuō),考慮當(dāng)前是否當(dāng)前正使能雙緩存doubleBufferingEnabled,是否不使用本地雙緩存nativeDoubleBuffering, BUFFER_STRATEGY_TYPE是否指定了每窗口緩存的雙緩存支持策略,如果沒(méi)有指定策略是否或本地windows系統(tǒng)環(huán)境沒(méi)有開(kāi)啟vista dwm效果,如果都滿足將使用BufferStrategyPaintManager,借由swing提供每窗口雙緩存機(jī)制,檢查swing記錄中是否具有有效緩存,若存在則會(huì)要求該區(qū)直接拷貝flip即可,如果沒(méi)有成功執(zhí)行雙緩存拷貝,則將加入Repaintmanager重畫(huà)區(qū)域進(jìn)行swing模式的重畫(huà)。
頂層容器除了在對(duì)等體發(fā)過(guò)消息后處理paint,也具有自己的repaint方法去主動(dòng)創(chuàng)造繪畫(huà)時(shí)機(jī)。
public void repaint(long time, int x, int y, int width, int height) {
       if (RepaintManager.HANDLE_TOP_LEVEL_PAINT) {//屬性swing.handleTopLevelPaint確定,默認(rèn)true
            RepaintManager.currentManager(this).addDirtyRegion(
                              this, x, y, width, height);
        }
        else {
            super.repaint(time, x, y, width, height);
        }
    }
這里的repaint將首先確定RepaintManager.HANDLE_TOP_LEVEL_PAINT-如果不支持將委托給Component.repaint,形成PaintEvent并進(jìn)行提交走AWT模式。支持的話將促使RepaintManager加入重畫(huà)區(qū)后通過(guò)調(diào)度走SWING模式。SWING模式就是走RepaintManager的方式。自身的repaint不會(huì)去考慮每窗口雙緩存直接拷貝區(qū)域,因?yàn)檫@時(shí)的需求就是要求重新繪畫(huà)。
輕量級(jí)swing組件在自己的repaint方法去主動(dòng)創(chuàng)造繪畫(huà)時(shí)機(jī)。
JComponent.Repaint{RepaintManager.currentManager(this).addDirtyRegion}走SWING模式處理。
SWING模式都是借由RepaintManager來(lái)安排繪畫(huà),它維護(hù)了一個(gè)幾何區(qū)域并負(fù)責(zé)重畫(huà)的框架。外界總是要求先加入RepaintManager重繪區(qū),在加入的同時(shí)激發(fā)起一個(gè)調(diào)度重畫(huà)的
SunToolkit.getSystemEventQueueImplPP(context).
                postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
                                              processingRunnable))

InvocationEvent。
注意,通過(guò)上文分析,對(duì)于頂層容器處理底層消息的觸發(fā)時(shí),走swing處理模式而通過(guò)swingpaintEventdispatcher去創(chuàng)建painitevent時(shí)除向repaintmanager登記臟區(qū)(如果不使用每窗口雙緩存策略)外,還要額外post一個(gè)IgnorePaintEvent。該paintevent在隨后的EDT里按awt模式走peer處理時(shí)并沒(méi)有加入awt的重畫(huà)臟區(qū),實(shí)際上忽略掉了繪制意義,這樣做避免了在swing和awt兩種模式的重復(fù)繪制,但同時(shí)形成依然將paint事件通知到組件的效果。
public void coalescePaintEvent(PaintEvent e) {
        Rectangle r = e.getUpdateRect();
        if (!(e instanceof IgnorePaintEvent)) {
            paintArea.add(r, e.getID());
        }

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多