Java Focus實現紀要二

          Posted on 2008-10-26 17:20 英雄 閱讀(1348) 評論(0)  編輯  收藏
           

          Java Focus實現紀要二

          1.       Jre1.7版本中,KeyboardFocusManagerDefaultKeyboardFocusManager這兩個類,Component, WComponentPeer類一起完成了focus的主要邏輯實現。

          DefaultKeyboardFocusManager是前者的系統默認實現。其單例注冊在appcontext,如果需要,程序員可以替代它,以擴展focus的邏輯實現。

          appcontext.put(KeyboardFocusManager.class, new SelfKeyboardFocusManager());

          2.       Component中提供了requestFocus方法。而各個組件在初始化時都會安裝默認的Listener。當這些Listener收到適當的事件通知后(比如mouse_press)即會調用這個方法。該方法首先判斷該組件是否focusable,組件所依托窗口是否focusable,當前聚焦組件的InputVerifier是否驗收輸入等等,判斷通過后請求重量級組件容器的peer.requestFocusWComponentPeer中提供該requestFocus方法。該方法首先調用native processSynchronousLightweightTransfer,其會調用KeyboardFocusManager .processSynchronousLightweightTransferr,作用是如果當前request組件的重量級組件容器正對應當前底層系統的聚焦組件,而且當前沒有任何切換焦點的heavyweightRequests,這時將直接切換focus變量KeyboardFocusManager.focusOwner

          如果上述調用沒有順利完成并返回true,則會調用native _requestFoucs。該方法會調用KeyboardFocusManager .shouldNativelyFocusHeavyweight,其作用就是完成request登記,并在登記時間戳以正確緩存處理后續進入EDTKeyevent處理。

          Request登記的結構為KeyboardFocusManager.heavyweightRequests=

          LinkedList< HeavyweightFocusRequest >

          -- HeavyweightFocusRequest{

          Component heavyweight;

          LinkedList<LightweightFocusRequest> lightweightRequests登記方式分為3:

          a. 如果發出requestFocus的組件的重量級組件容器正對應當前底層系統的聚焦組件,而且當前沒有任何切換焦點的heavyweightRequest,則增加一個heavyweightRequest并向Post-Qqeue post focus-event

          b. 如果發出requestFocus組件的重量級組件容器不對應當前底層系統的聚焦組件,而且當前沒有任何切換焦點的heavyweightRequest;或者當前存在切換焦點的heavyweightRequest,而最后一個heavyweightRequest. Heavyweight!=當前request組件的重量級組件容器,則要增加一個heavyweightRequest,并同步通知底層系統進行重量級對等組件的focus切換

          c. 如果當前存在切換焦點的heavyweightRequest,而且最后一個heavyweightRequest. Heavyweight==當前requestFocus的組件的重量級組件容器,則直接在request.lightweightRequests追加一個LightweightFocusRequest

          3.       EDT在逐個處理AWTEvent時,委托給EventQueue.dispatchEvent,繼而委托給Component. dispatchEventImpl,該方法順序執行下面的代碼片段:

          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~·

          /* focusManagerIsDispatching標志了該event;如果==true意味著該event不會交由KeyboardManager進行retargetdispatch。而這兩個動作主要完成的功能就是刷新java的全局focus變量。因此可以想象focusManagerIsDispatching==trueFocus_eventfocus發生切換后的event,而focusManagerIsDispatching==false的是PrepareFocusEvent*/

                      if (!e.focusManagerIsDispatching) {//----------PrepareFocusEvent

                      // Invoke the private focus retargeting method which provides

                      // lightweight Component supportF

                      /*通過retargetFocus,處理之前注冊的request請求,最終激發出合適的CausedFocusEvent,交給下面的dispatch.

           */

                      if (e.isPosted) {

                          e = KeyboardFocusManager.retargetFocusEvent(e);

                          e.isPosted = true;

                      }

                      // Now, with the event properly targeted to a lightweight

                      // descendant if necessary, invoke the public focus retargeting

                      // and dispatching function

          /*通過dispatch給注冊的DefaultKeyboardFocusManager,最終更新了java的全局focus變量

           */

                      if (KeyboardFocusManager.getCurrentKeyboardFocusManager().

                          dispatchEvent(e))

                      {

                          return;

                      }

                  }

          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~·

          4.       總之:

          a. 輕量級組件的Mouse_Press ListenerrequestFocusrequest通過必要條件檢查后會在KeyboardFocusManager.heavyweightRequests緩存列表登記,同時在一個列表中登記一個時間戳marker=當前系統時間。

          b. 每一個KeyEvent都有一個發生時間when,這個發生時間認為是AWT-Windows loop底層event形成KeyEvent的時間。EDT在調用KeyboardFocusManager dispatchEvent處理一個KeyEvent時只要發現when晚于時間戳緩存列表中登記的第一個時間戳,就充分說明這是在某焦點切換請求發出后發生的鍵盤事件,則不應該將這個KeyEvent target到當前全局focus變量,因而這時暫將此KeyEvent緩存在另一個列表。

          c. 根據前面的分析,在requestFocus時有3種情況,一種是新增heavyweightRequest,同時postpost-queue一個FocusEvent,一種是新增heavyweightRequest,同時因為要求底層系統切換重量級對等體而awt-loop到一個FocusEvent,這兩種情況的request都在列表中期待對應FocusEvent到來再切換焦點。從登記時間戳開始,被awt-loop到的KeyEvent進入EDT時都將緩存下來,而一段時間后當期待的FocusEventpost-queue進入event-queue并要在EDT中處理時,有充分的條件可以判斷出此后再進入EDTKeyEvent,都至少是request登記時間戳后發生的,則這時可以完成此request-刪除該heavyweightRequest緩存條目,做focus實際切換,并將緩存的KeyEvent 及時間戳記錄處理掉。而第3種情況是在requestFocus時可以在最末一個heavyweightRequest上直接追加LightWeightReuquest,那么當該heavyweightRequest期待的FocusEvent到來時,按前面所述處理完該request,再將后續LightWeightRequest保存引用到一個全局變量KeyboardFocusManager.currentLightweightRequests,再將此刻為止awt-loop至的post-queue的所有event完全flushevent-queue,再把一個要求循環處理所有currentLightweightRequests指向的LightWeightRequestsInvocationEvent post event-queue之后。這樣當EDT開始處理該InvocationEvent時,有充分的條件可以判斷出此后再進入EDTKeyEvent,都至少是最后一個后續LightWeightRequest登記時間戳后發生的,則這時只需按該InvocationEvent執行即可,及逐個清理LightWeightRequest完成focus切換及處理時間戳和緩存KeyEvent。如果在循環處理過程中發生對某一個組件requestFocus調用,這時會根據處理之初currentLightweightRequests中是否只有單獨1request來確定能否processSynchronousLightweightTransferr即如果有多個,則這時禁止processSynchronousLightweightTransferr以防止破壞了切換焦點的順序。

          d. 重量級組件不需要在Mouse_Press Listener request Focus,當被進行Mouse Press時,底層系統分發一個Focus Event,當進入EDT處理時,在jre1.7中通過KeyboardFocusManager.retargetUnexpectedFocusEventretarget首先逐個剔除request后進行期待匹配(針對可能的底層分發-post-queue-event-queue中間環節Event的遺漏等例外情形),如果最后沒有一個request匹配,則直接形成CausedFocusEvent交給后繼dispatch完成焦點切換。更確切地說,對于jre1.7而言組件聚焦應該都通過requestFocus完成切換,不通過該方式的聚焦切換在retarget時將歸屬到Unexpected被處理,而重量級組件的這種聚焦正好通過unexpected完成。

          5.       最后,個人認為jre1.7中存在一個可能的問題:每次dispatchEvent時都會在retargetFocusEventprocessCurrentLightweightRequests這樣不久破壞了4-c分析的時機邏輯了么?為什么要這樣呢?


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


          網站導航:
           
          主站蜘蛛池模板: 定远县| 青州市| 平远县| 芦山县| 婺源县| 蓝田县| 南投县| 承德县| 克东县| 昌图县| 枣强县| 景谷| 盘锦市| 唐山市| 印江| 正安县| 新余市| 广西| 揭阳市| 法库县| 阳朔县| 肥东县| 河北省| 莱西市| 巨鹿县| 南岸区| 太仆寺旗| 崇信县| 云梦县| 高台县| 上高县| 邓州市| 英吉沙县| 赞皇县| 友谊县| 交口县| 稷山县| 家居| 育儿| 县级市| 宾阳县|