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()); } |
|
來(lái)自: xuanda007 > 《應(yīng)用》