處理事件(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>
的簡寫,它是透過INotifyValueChange
interface來為VisualElement推測正確的Type T
1 | // 為myIntegerField註冊一個監聽值改變的OnIntegerFieldChange callback |
更改Value有以下方式
- 直接更改
value
屬性,這會觸發一個新的ChangeEvent
myControl.value = myNewValue;
- 使用該element的
SetValueWithoutNotify()
更改,這不會觸發一個新的ChangeEvent
myControl.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