處理事件(Handle Event)
UI Toolkit Events類似於HTML Events,當事件(Event)發生時,該事件就會發送給target visual element以及在事件傳播路徑(propagation path)上的visual elements。
處理事件的順序如下:
- 由root element往下直到目標(target)的parent element,都會執行event callback,稱為
trickle-down phase - 在事件目標執行event callback,稱為
target phase - 在事件目標上面呼叫
ExecuteDefaultActionAtTarget() - 由事件目標的parent element往上直到root element都會執行event callbacks,稱為
bubble-up phase - 在事件目標上面呼叫
ExecuteDefaultAction()
註冊Event Callbacks(Register an event callback)
可以在class中註冊callback,為個別的instance自訂行為(behavior),例如滑鼠點擊text label時作出反應。
除了目標(target)以外,沿著傳播路徑經過的element都會接收到兩次event
- 一次是在trickle-down phase
- 另外一次是bubble-up phase
使用element的RegisterCallback方法來註冊一個event callback
element.RegisterCallback<MouseDownEvent>(CallbackMethod);此例中把CallbackMethod註冊到MouseDownEvent
1 | myElement.RegisterCallback<MouseDownEvent>(MyCallback); |
預設中,註冊的event callback會在target phase與bubble-up phase的時候執行,這樣可以確保父element會在子element之後做出反應。
- 如果你想要父element在子element之前做出反應可以在註冊event時,將其設定為
TrickleDown.TrickleDown
1 | using UnityEngine; |
在一個element中,你可以對同一個event註冊多個callback,但是你只能在同一事件和傳播階段註冊一次相同的callback。
有參數的callback
1 | // 此將會向MyCallbackWithData傳送一個MyType的參數 |
移除註冊的callback
使用myElement.UnregisterCallback()移除註冊的callback。
監聽value
UI controls會使用value屬性來存放資料,例如
Toggle會持有一個boolean屬性,當Toggle關閉(off)時設為false,開啟(on)時設為true。IntegerField會持有一個integer屬性,用來保存該field的值。int val = myIntegerField.value;
- 透過
RegisterValueChangedCallback你可以註冊一個監聽ChangeEvent事件的callbackRegisterValueChangedCallback是RegisterCallback<ChangeEvent>的簡寫,它是透過INotifyValueChangeinterface來為VisualElement推測正確的Type T
1 | // 為myIntegerField註冊一個監聽值改變的OnIntegerFieldChange callback |
更改Value有以下方式
- 直接更改
value屬性,這會觸發一個新的ChangeEventmyControl.value = myNewValue;
- 使用該element的
SetValueWithoutNotify()更改,這不會觸發一個新的ChangeEventmyControl.SetValueWithoutNotify(myNewValue);
獲取Pointer
當visual element獲取一個Pointer時,Unity會向這個visual element發送所有與這個Pointer相關的Events,而不管是否這個Pointer懸停(hovers over)在這個visual element上,舉例來說,假設你建立了一個control,他會接收drag events並獲取pointer,不論pointer的位置為何,這個control都會接收drag events
Event與自訂Control
當自訂Control時,有兩種方式回應UI Toolkit events
- 註冊一個event callback
- 實作一個default action
callbacks與default action的差異
- callback必須要註冊在一個實體物件上;default action則是類似在class上的virtual functions
- 所有在傳播路徑(propagation path)上的visual element都會執行callback;Default actions只會在event target上執行。
- callbacks可能會需要額外檢查是否是需要做出反應的event;default actions可以省略這個步驟
- callbacks效能可能較差,default actions會好一些
- 因為callbacks在propagation phase時需要在註冊表(callback registry)裡面搜尋,而default actions則不需要
實作Default Action
若要實作Default Action的話,需要繼承VisualElement,並實作ExecuteDefaultActionAtTarget()或是ExecuteDefaultAction(),或兩個方法都實作。
如果實作了Default Action,會應用到該class的所有實體(instances)
- 如果你想要在parent callback之前執行default action可以把default action放到
ExecuteDefaultActionAtTarget() - 應該將default actions視為該element type要有的行為(behaviors),例如:chcekbox觸發click event更新它的狀態時應override default action,而不是為所有checkbox註冊一個callback
1 | override void ExecuteDefaultActionAtTarget(EventBase evt) |
custom controls的最佳實踐
- 如果是element有的行為(behaviors),你應該實作在
default action - 在callback中,你可以使用
PreventDefault()來取消該element的default action。 - 若一個instance沒有callback,那麼它不會進入
propagation process - 在事件分派過程(event dispatch process)中,有兩個時段可以執行你的default action
- 若想在
trickle-down與bubble-up之間,在target callbacks之後立刻執行的話,override這個ExecuteDefaultActionsAtTarget()方法 - 在事件分派過程(event dispatch process)結束時,override這個
ExecuteDefaultActions()方法
- 若想在
- 如果可能的話,盡量將default action寫在
ExecuteDefaultActions(),在事件傳播過程(event propagation process)中的trickle-down階段或是bubble-up階段,可以透過PreventDefault()來覆蓋預設的操作。 - 如果一個事件不應該傳播到parent element的話,你必須要把傳播停下
- 例如:一個TextField在收到一個
KeyDownEvent去修改它的值時,不應該讓這個事件傳播到它的parent visual element - 使用
ExecuteDefaultActionsAtTarget()實作default action時,可以呼叫StopPropagation()這可以確保event在bubble-up階段時不會處理
- 例如:一個TextField在收到一個
- Default actions僅針對event target執行,要讓class對target的child element或parent element的事件作出反應,則必須在event的
trickle-down或是bubble-up傳播階段之一註冊callback以接收該事件。 - 為了提高性能,盡量避免在的class中註冊回調函式。
- 一個parent panel可以在trickle-down階段停止傳播,避免事件傳到它的children
- 你無法在event class本身中阻止
EventBase.PreDispatch()和EventBase.PostDispatch()方法的執行 - 可以透過以下方法事件傳播(event propagation)
StopImmediatePropagation():立刻停止事件傳播,其他callback不會執行這個event,但是ExecuteDefaultActionAtTarget()與ExecuteDefaultAction()default actions 仍然會執行。StopPropagation():在這個element上的最後一個callback執行完之後,停止傳播。這可以確保所有在這個element上的callback都執行完,不會有更多element去對這個事件做出反應。ExecuteDefaultActionAtTarget()與ExecuteDefaultAction()default actions 仍然會執行。PreventDefault():在事件傳播過程(event propagation process)時,避免呼叫ExecuteDefaultActionAtTarget()與ExecuteDefaultAction()。PreventDefault()不會阻止其他callback的執行。- 在bubble-up階段時,
PreventDefault()不會影響ExecuteDefaultActionAtTarget()action。
Reference:https://docs.unity3d.com/Manual/UIE-Events-Handling.html