关于源码
关于源码可以参考这篇博客下载或调试。
Selectable
特性
[AddComponentMenu("UI/Selectable", 70)] [ExecuteInEditMode] [SelectionBase] [DisallowMultipleComponent]
AddComponentMenu – 为Component菜单添加选项,顺序为70。当点击一个GameObject的AddComponent按钮时,会弹出一个菜单,点击UI选项,就可以看到排在最后一个的就是Selectable。
ExecuteInEditMode – 该组件会在编辑器下执行。
SelectionBase – 标记该对象为选择基本对象。
DisallowMultipleComponent – 不允许一个GameObject上出现同种组件。我们无法为一个对象添加两个Selectable组件,也无法为一个Button对象再添加一个InputField组件。
属性
- 继承自UIBehaviour,并且继承了7个接口。
public class Selectable : UIBehaviour, IMoveHandler, IPointerDownHandler, IPointerUpHandler, IPointerEnterHandler, IPointerExitHandler, ISelectHandler, IDeselectHandler
UIBehaviour – 所有UI组件的基类,它被放在EventSystem目录下,除了IsDestroyed以外,其他都是虚函数,我们也可以认为它是事件系统的一部分,它添加了Awake(当脚本实例被加载,即AddComponent时)、OnRectTransformDimensionsChange(当RectTransform维度变化)等方法来接收来自UnityEngine 的事件。
Selectable – Awake 时获取了一个Graphic 的组件实例m_TargetGraphic(Image组件间接继承自Graphic)。如果我们选择的Transition为Color Tint,当Selectable的状态变化(普通、高亮、按压、禁用)时,会调用m_TargetGraphic的CrossFadeColor方法,即将当前图像渐变为指定颜色。
OnEnable – 会将该实例添加至Selectable 的静态列表s_List 里(s_List里存放了所有可用的Selectable,用于UI导航的查询),然后设置实例的状态为Normal 或者Highlighted(亦或Disabled)。
OnDisable – 将实例清除状态(需要恢复颜色和图片,以及播放Normal 动画),并从s_List 中将该实例移除。
OnDidApplyAnimationProperties – (当应用动画属性后),会通过OnSetProperty 方法调用InternalEvaluateAndTransitionToSelectionState,刷新当前状态。
OnCanvasGroupChanged – (当CanvasGroup 变化时)里,会判断新的GanvasGroup 的interactable,如果GanvasGroup 的interactable为false,那么Selectable 本身也就被禁用了。接着,刷新当前状态。
继承自IMoveHandler – 实现OnMove 方法。根据移动方向,导航到下一个Selectable组件。
继承自IPointerDownHandler – 实现OnPointerDown 方法。调用
EventSystem.current.SetSelectedGameObject
将自己设为当前选中对象(会调用自己的OnSelect和前任对象的OnDeselect),标记isPointerDown为true 并刷新状态(当isPointerInside和isPointerDown同时为true的时候为Pressed 状态)。继承自IPointerUpHandler – 实现OnPointerUp方法。标记isPointerDown为false并刷新状态。
继承自IPointerEnterHandler – 实现OnPointerEnter方法。标记isPointerInside为true并刷新状态。
继承自IPointerExitHandler – 实现OnPointerExit方法。标记isPointerInside为false并刷新状态。
继承自ISelectHandler – 实现OnSelect方法。标记hasSelection为true并刷新状态(hasSelection为true时为Highlighted状态,另外isPointerInside和isPointerDown也是判断Highlighted状态的依据)。
继承自IDeselectHandler – 实现OnDeselect方法。标记hasSelection为false并刷新状态。
关于IsHighlighted:
protected bool IsHighlighted(BaseEventData eventData) { if (!IsActive()) return false; if (IsPressed()) return false; bool selected = hasSelection; if (eventData is PointerEventData) { var pointerData = eventData as PointerEventData; selected |= (isPointerDown && !isPointerInside && pointerData.pointerPress == gameObject) // This object pressed, but pointer moved off || (!isPointerDown && isPointerInside && pointerData.pointerPress == gameObject) // This object pressed, but pointer released over (PointerUp event) || (!isPointerDown && isPointerInside && pointerData.pointerPress == null); // Nothing pressed, but pointer is over } else { selected |= isPointerInside; } return selected;
参数为输入模块传递过来的事件数据,主要用于判断事件响应对象是否是本对象(或者是null)。上述的OnPointerDown等方法是通过EvaluateAndTransitionToSelectionState方法评估并刷新状态。在这个方法里,UpdateSelectionState调用IsPressed和IsHighlighted判断当前状态。InternalEvaluateAndTransitionToSelectionState判断当前组件是否被禁用,然后调用DoStateTransition方法。
protected virtual void DoStateTransition(SelectionState state, bool instant) { Color tintColor; Sprite transitionSprite; string triggerName; switch (state) { case SelectionState.Normal: tintColor = m_Colors.normalColor; transitionSprite = null; triggerName = m_AnimationTriggers.normalTrigger; break; case SelectionState.Highlighted: tintColor = m_Colors.highlightedColor; transitionSprite = m_SpriteState.highlightedSprite; triggerName = m_AnimationTriggers.highlightedTrigger; break; case SelectionState.Pressed: tintColor = m_Colors.pressedColor; transitionSprite = m_SpriteState.pressedSprite; triggerName = m_AnimationTriggers.pressedTrigger; break; case SelectionState.Disabled: tintColor = m_Colors.disabledColor; transitionSprite = m_SpriteState.disabledSprite; triggerName = m_AnimationTriggers.disabledTrigger; break; default: tintColor = Color.black; transitionSprite = null; triggerName = string.Empty; break; } if(gameObject.activeInHierarchy) { switch (m_Transition) { case Transition.ColorTint: StartColorTween(tintColor * m_Colors.colorMultiplier, instant); break; case Transition.SpriteSwap: DoSpriteSwap(transitionSprite); break; case Transition.Animation: TriggerAnimation(triggerName); break; } } }
根据状态设置颜色、图片或动画名称,再通过StartColorTween、DoSpriteSwap 或TriggerAnimation方法在UI上体现出状态的改变。
Button
Button继承自Selectable并额外继承了IPointerClickHandler、ISubmitHandler两个接口。
它还添加了一个UnityEvent类型的事件onClick。onClick事件可以添加用户自定义的监听,具体方法可以通过编辑器添加也可以通过
onClick.AddListener
添加。就是相对于Selectable添加了响应点击和确认事件的接口,并且开放了可添加用户自定义监听的onClick事件。
OnPointerClick会调用Press方法来回调onClick。
OnSubmit也会调用Press方法,并切换状态为Pressed,并开启协程调用OnFinishSubmit,让状态渐变为当前状态(通过UpdateSelectionState获得的状态)。
最后
Selectable的作用在于提供了基于鼠标事件的四种状态变化。一方面,为Button、Dropdown等派生类提供了基础的逻辑,另一方面,我们也可以根据Selectable派生出新的自定义组件。