Java Focus实现纪要二

Posted on 2008-10-26 17:20 英雄 阅读(1328) 评论(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分析的时机逻辑了么?为什么要这样呢?


只有注册用户登录后才能发表评论。


网站导航: