1、Selectable
Selectable是UGUI的核心组件,除了最常用的Button,它还是Scrollbar、Dropdown、Slider、Toggle、InputField这些组件的基类。
在Selectable之前,我们看到,它被添加了四个属性:
[AddComponentMenu("UI/Selectable", 70)]
[ExecuteInEditMode] 运行在编辑器模式下
[SelectionBase] 标记此对象为基本选择对象
[DisallowMultipleComponent] 一个Game Object上不允许有多个Selectable组件,同样我们也无法为一个Button组件添加一个InputFiled组件。
Selectable继承自UIBehaviour,并且继承了7个接口。
public class Selectable : UIBehaviour,IMoveHandler,IPointerDownHandler, IPointerUpHandler,
IPointerEnterHandler, IPointerExitHandler,ISelectHandler, IDeselectHandler
UIBehaviour
是所有UI组件的基类,继承了MonoBehaviour,它被放在EventSystem目录下,除了IsDestroyed以外,其他都是虚函数,我们也可以认为它是事件系统的一部分,它添加了Awake(当脚本实例被加载,即AddComponent时)、OnRectTransformDimensionsChange(当RectTransform尺寸变化)等方法来接收来自UnityEngine的事件。
Selectable维护了一个在场景中处于激活状态的所有可选择对象的列表:
private static List<Selectable> s_List = new List<Selectable>();
Selectable继承UIBehaviour的几个接口
- Selectable的Awake()方法获取一个Graphic组件实例m_TargetGraphic,如果选择的Transition是Color Tint,当Selectable的状态变化(普通、高亮、按压、禁用)时,会调用m_TargetGraphic.CrossFadeColor方法,将当前颜色渐变成指定颜色。
- OnEnable方法中,会把实例本身加入到Selectable的静态列表s_List 中,然后设置实例的状态为Normal或者Highlighted。
- OnDisable方法中,会把实例本身从Selectable的静态列表s_List 中移除,并将实例状态清除重置,包括恢复颜色和图片,还有播放normal动画。
- 当动画的属性发生改变后,会调用OnDidApplyAnimationProperties方法,在方法中调用OnSetProperty方法的InternalEvaluateAndTransitionToSelectionState方法中的DoStateTransition方法,刷新状态。
- 当CanvasGroup发生变化时,会调用OnCanvasGroupChanged方法,会判断新的GanvasGroup的interactable,如果GanvasGroup的interactable为false,那么Selectable本身也就被禁用了。接着,刷新当前状态。
然后我们看Selectable继承的其他几个接口
- 继承自IMoveHandler需要实现OnMove方法。根据移动方向,导航到下一个Selectable组件。
- 继承自IPointerDownHandler需要实现OnPointerDown方法。调用EventSystem.current.SetSelectedGameObject将自己设为当前选中对象(会调用自己的OnSelect和旧对象的OnDeselect),标记isPointerDown为true并刷新状态(当isPointerInside和isPointerDown同时为true的时候,为Pressed状态,IsPressed()方法判断是否为Pressed状态)。
- 继承自IPointerUpHandler需要实现OnPointerUp方法。标记isPointerDown为false并刷新状态。
- 继承自IPointerEnterHandler需要实现OnPointerEnter方法。标记isPointerInside为true并刷新状态。
- 继承自IPointerExitHandler需要实现OnPointerExit方法。标记isPointerInside为false并刷新状态。
- 继承自ISelectHandler需要实现OnSelect方法。标记hasSelection为true并刷新状态(hasSelection为true时为Highlighted状态,另外isPointerInside和isPointerDown也是判断Highlighted状态的依据,IsHighlighted()方法判断是否是高亮状态)。
- 继承自IDeselectHandler需要实现OnDeselect方法。标记hasSelection为false并刷新状态。
bool IsHighlighted(BaseEventData eventData)
参数是输入模块传入的事件数据,主要判断事件响应对象是否是本对象,或者是为null。然后会跟据hasSelection与isPointerInside和isPointerDown做或运算,处理不同的状态
- 对象被按下,但是指针移开了
- 对象被按下,但是指针松开了
- 没有对象被按下,但是指针在对象上面
上述的OnPointerDown等方法是通过EvaluateAndTransitionToSelectionState方法评估并刷新状态。在这个方法里,UpdateSelectionState调用IsPressed和IsHighlighted判断当前状态。InternalEvaluateAndTransitionToSelectionState判断当前组件是否被禁用,然后调用DoStateTransition方法。根据状态设置颜色、图片或动画名称,再通过StartColorTween、DoSpriteSwap或TriggerAnimation方法在UI上体现出状态的改变。
Selectable的作用
Selectable的作用在于提供了基于鼠标事件的四种状态变化。一方面,为Button、Dropdown等派生类提供了基础的逻辑,另一方面,我们也可以根据Selectable派生出新的自定义组件。
2、Button
Button继承了Selectable,IPointerClickHandler,ISubmitHandler,还添加了一个UnityEvent类型的事件onClick,onClick事件可以通过onClick.AddListener方法,添加用户自定义的监听。
OnPointerClick方法,会通过Press方法回调onClick。
OnSubmit方法,也会调用Press方法回调onClick,并设置状态为Pressed,开启协程OnFinishSubmit,让状态渐变为当前状态(通过UpdateSelectionState获得的状态)。
可以看出Button就是相对于Selectable添加了响应点击和确认事件的接口,并且开放了可添加用户自定义监听的onClick事件。