JAVA MouseEvent實現紀要
Jre1.7鼠標事件以MouseEvent類封裝。共有如下8種:
/**
*The"mouseclicked"event.This<code>MouseEvent</code>
*occurswhenamousebuttonispressedandreleased.
*/
publicstaticfinalintMOUSE_CLICKED = MOUSE_FIRST;
/**
*The"mousepressed"event.This<code>MouseEvent</code>
*occurswhenamousebuttonispusheddown.
*/
publicstaticfinalintMOUSE_PRESSED = 1 + MOUSE_FIRST; //Event.MOUSE_DOWN
/**
*The"mousereleased"event.This<code>MouseEvent</code>
*occurswhenamousebuttonisletup.
*/
publicstaticfinalintMOUSE_RELEASED = 2 + MOUSE_FIRST; //Event.MOUSE_UP
/**
*The"mousemoved"event.This<code>MouseEvent</code>
*occurswhenthemousepositionchanges.
*/
publicstaticfinalintMOUSE_MOVED = 3 + MOUSE_FIRST; //Event.MOUSE_MOVE
/**
*The"mouseentered"event.This<code>MouseEvent</code>
*occurswhenthemousecursorenterstheunobscuredpartofcomponent's
*geometry.
*/
publicstaticfinalintMOUSE_ENTERED = 4 + MOUSE_FIRST; //Event.MOUSE_ENTER
/**
*The"mouseexited"event.This<code>MouseEvent</code>
*occurswhenthemousecursorexitstheunobscuredpartofcomponent's
*geometry.
*/
publicstaticfinalintMOUSE_EXITED = 5 + MOUSE_FIRST; //Event.MOUSE_EXIT
/**
*The"mousedragged"event.This<code>MouseEvent</code>
*occurswhenthemousepositionchangeswhileamousebuttonispressed.
*/
publicstaticfinalintMOUSE_DRAGGED = 6 + MOUSE_FIRST; //Event.MOUSE_DRAG
/**
*The"mousewheel"event. Thisistheonly<code>MouseWheelEvent</code>.
*Itoccurswhenamouseequippedwithawheelhasitswheelrotated.
*@since1.4
*/
publicstaticfinalintMOUSE_WHEEL = 7 + MOUSE_FIRST;
鼠標事件由AWT-Windows線程從底層系統獲取到并Flush給EDT進行處理。EDT在處理時按一下步驟進行:
1. EventDispatchThread中執行SunDragSourceContextPeer.checkEvent。其功能含義為拖拽開始時將忽略掉當時還在Post Event Queue等待處理的單純鼠標事件。雖然MouseEvent和拖拽的事件有著千絲萬縷的聯系,但是在拖拽過程中并不會影響MouseEvent的dispatch.
此前對java拖拽的實現分析已經知道,拖拽開始后AWT-loop到的底層事件將由一個DragSourcePeer在底層處理后直接包裝成DragSourceEvent發布到EDT中進行處理,同時由一個DropTargetPeer在底層處理后包裝成SunDropTargetEvent發布到EDT中進行處理。SunDropTargetEvent是MouseEvent的子類,而DragSourceEvent就不是。這里面的原因是因為拖拽過程中系統只有一個DragSource,因此DragSourceEvent的target一定是此DragSource組件,只要在拖拽開始時通過一個全局變量記錄下來,在封裝過程source=DragSourceEvent直接定位到組件即可,但是同一個拖拽過程卻可能面臨多個DropTarget,而且最關鍵的是就此底層事件底層系統只能給出對重量級組件的定位,因此對于SunDropTargetEvent需要經過類似MouseEvent的retarget過程確定目標組件,所以SunDropTargetEvent定義為MouseEvent的子類。當然SunDropTargetEvent在經過retarget后再形成具有準確目標組件的DropTargetEvent就和DragSourceEvent一樣不再是MouseEvent的子類了。可見,SunDropTargetEvent可以看作是一個中間過渡事件,該類事件存在的主要意義就是借助EDT對MouseEvent的統一retarget過程;也因而在EventQueue,EventDispatchThread類的處理中特別針對該事件有特殊處理。
正因為SunDropTargetEvent的存在,checkEvent這個方法的實現才是忽略單純的鼠標事件,但不能忽略了SunDropTargetEvent。
if (discardingMouseEvents && event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
if (!(mouseEvent instanceof SunDropTargetEvent)) {
returnfalse;
}
}
returntrue;
2. EventQueue中((Component)src).dispatchEvent(event);而在Component中dispatchEventImpl。如果該組件是一個Container,將進入Container. dispatchEventImpl,該方法將通過Container. LightweightDispatcher進行dispatchEvent,其主要功能就是要retarget及在之后針對Mouse_Move updateCursor.
Awt-windows loop到的Mouse Event只能是針對重量級組件的,如果本次點擊是在某重量級組件比如JFRAME的一個JTEXTFIELD上點擊,需要通過這個過程將此Mouse Event定位source=JTEXTFIELD;同時可能一個重量級組件的MouseMove對應其包含的兩個輕量級組件的exit和enter;所有這些具體retarget在processMouseEvent中完成;完成邏輯主要根據鼠標事件的坐標和container的子組件記錄,以及組件提供的publicboolean contains(int x, int y) 方法去尋找最頂層包含此坐標的子組件;除了找到該組件外,再根據一個targetLastEntered域變量比較是否發生了變化,如果是則要產生Exit和Enter.
3. Retargeted Mouse Event將進入Component. dispatchEventImpl.
A.如果是SunDropTargetEvent將處理掉并返回。
B.MOUSE_WHEEL將由peer處理后可能被dispatchMouseWheelToAncestor,即交由該組件的合適的容器組件處理。
C.Allow the Toolkit to pass this to AWTEventListeners,即交給Toolkit注冊listener處理。
D.newEventsOnly&& eventEnabled則交給該組件對應listener處理。
E.newEventsOnly will be false for a listenerless ScrollPane, but
MouseWheelEvents still need to be dispatched to it so scrolling
can be done. autoProcessMouseWheel方法提供了一個處理鼠標輪滾動的切入點,即如果scrollpane沒有注冊任何監聽導致newEventsOnly =false,也可以實現autoProcessMouseWheel方法來實現通用的scroll。