數(shù)據(jù)加載中……
          [轉(zhuǎn)]JAVA Painting-Swing實(shí)現(xiàn)紀(jì)要一

          JAVA 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)被封裝為多種組件類的實(shí)例,而這些組件類是有合理的繼承關(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給出了要加載的類,在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類,其將在類加載初始化時(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)完成相應(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成員類并建立實(shí)例,以后的paint回調(diào)等都推托給ui成員類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)的概念,類似于在進(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)了。
          下面在具體分析各類事件的處理。
          對(duì)于頂層容器的受底層事件消息的觸發(fā),當(dāng)?shù)玫降耐ㄖ且驗(yàn)閑xpose暴露隱藏區(qū)(暴露被遮蔽的部分或恢復(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)類初始化過(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é)繪制的核心類。
          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)自repaint-UPDATE類型),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());
                  }



          ---------------------------------------------------------------------------------------
          ——使你疲勞的不是遠(yuǎn)方的高山,而是你鞋里一粒沙子!

          posted on 2009-10-20 09:58 鋒行 閱讀(644) 評(píng)論(0)  編輯  收藏


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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 延吉市| 丹棱县| 治县。| 滨海县| 温泉县| 化州市| 昌都县| 新绛县| 莱芜市| 女性| 绥中县| 古田县| 红原县| 普格县| 赤壁市| 泉州市| 交城县| 乌鲁木齐市| 泗阳县| 航空| 昭苏县| 新野县| 琼中| 区。| 防城港市| 辽阳市| 宝鸡市| 瑞安市| 昭觉县| 阿拉善盟| 绥宁县| 琼结县| 宝山区| 鄂州市| 安宁市| 黄陵县| 台湾省| 繁昌县| 洞头县| 和龙市| 望江县|