从Unity的PointerEventData源码分析触摸、点击UI事件(2) 

上一篇介绍了PointerEventData类的字段及其用途,这一篇来详细说明它们是怎样运作的。

关键接口IEventSystemHandler

Unity通过继承关键接口IEventSystemHandler的接口,来定义了所有用于处理触摸、点击的事件。

IPointerEnterHandler 进入事件,IPointerExitHandler 离开事件,IPointerDownHandler 按下事件,IPointerUpHandler 抬起事件,IPointerClickHandler 点击事件,IInitializePotentialDragHandler 找到可拖动对象后, 真正开始拖动之前的事件, 整个拖动过程中只发送一次,IBeginDragHandler 开始拖动事件,IDragHandler 拖动事件,IEndDragHandler 结束拖动事件,IDropHandler 拖动并放开事件,IScrollHandler 滚动事件,IUpdateSelectedHandler 更新选中事件,ISelectHandler 切换选中事件,IDeselectHandler 取消选中事件,IMoveHandler 导航移动事件,ISubmitHandler 导航提交事件,ICancelHandler 导航取消事件

关键类EventTrigger

还可以通过EventTrigger类来实现上面接口的效果,EventTrigger类借助枚举EventTriggerType,与上面的接口一一对应。

   public enum EventTriggerType
   {
       /// <summary>
       /// Intercepts a IPointerEnterHandler.OnPointerEnter.
       /// </summary>
       PointerEnter = 0,

       /// <summary>
       /// Intercepts a IPointerExitHandler.OnPointerExit.
       /// </summary>
       PointerExit = 1,

       /// <summary>
       /// Intercepts a IPointerDownHandler.OnPointerDown.
       /// </summary>
       PointerDown = 2,

       /// <summary>
       /// Intercepts a IPointerUpHandler.OnPointerUp.
       /// </summary>
       PointerUp = 3,

       /// <summary>
       /// Intercepts a IPointerClickHandler.OnPointerClick.
       /// </summary>
       PointerClick = 4,

       /// <summary>
       /// Intercepts a IDragHandler.OnDrag.
       /// </summary>
       Drag = 5,

       /// <summary>
       /// Intercepts a IDropHandler.OnDrop.
       /// </summary>
       Drop = 6,

       /// <summary>
       /// Intercepts a IScrollHandler.OnScroll.
       /// </summary>
       Scroll = 7,

       /// <summary>
       /// Intercepts a IUpdateSelectedHandler.OnUpdateSelected.
       /// </summary>
       UpdateSelected = 8,

       /// <summary>
       /// Intercepts a ISelectHandler.OnSelect.
       /// </summary>
       Select = 9,

       /// <summary>
       /// Intercepts a IDeselectHandler.OnDeselect.
       /// </summary>
       Deselect = 10,

       /// <summary>
       /// Intercepts a IMoveHandler.OnMove.
       /// </summary>
       Move = 11,

       /// <summary>
       /// Intercepts IInitializePotentialDrag.InitializePotentialDrag.
       /// </summary>
       InitializePotentialDrag = 12,

       /// <summary>
       /// Intercepts IBeginDragHandler.OnBeginDrag.
       /// </summary>
       BeginDrag = 13,

       /// <summary>
       /// Intercepts IEndDragHandler.OnEndDrag.
       /// </summary>
       EndDrag = 14,

       /// <summary>
       /// Intercepts ISubmitHandler.Submit.
       /// </summary>
       Submit = 15,

       /// <summary>
       /// Intercepts ICancelHandler.OnCancel.
       /// </summary>
       Cancel = 16
   }

最简单的使用EventTrigger:

   void Start() 
   {
        EventTrigger eventTrigger = GetComponent<EventTrigger>();
        if (!eventTrigger) eventTrigger = gameObject.AddComponent<EventTrigger>();

        var entry1 = new EventTrigger.Entry();
        entry1.eventID = EventTriggerType.PointerClick;//可修改类型 
        //等同于在物体的Monobehavior继承IPointerClickHandler
        entry1.callback.AddListener((eventData => {
            var pointerEventData = eventData as PointerEventData;
            Debug.LogError("----- PointerClick");
        }));
   }

触摸、点击的运行逻辑

通过ExecuteEvents这个静态事件类,执行Execute方法,例如 OnPointerEnter进入事件:

 public static class ExecuteEvents
 {
     public delegate void EventFunction<T1>(T1 handler, BaseEventData eventData);

     public static T ValidateEventData<T>(BaseEventData data) where T : class
     {
         if ((data as T) == null)
             throw new ArgumentException(String.Format("Invalid type: {0} passed to event expecting {1}", data.GetType(), typeof(T)));
         return data as T;
     }

     private static readonly EventFunction<IPointerEnterHandler> s_PointerEnterHandler = Execute;

     private static void Execute(IPointerEnterHandler handler, BaseEventData eventData)
     {
         handler.OnPointerEnter(ValidateEventData<PointerEventData>(eventData));
     }
  }

类似的,对之前提到的所有的事件类型,都提供了对应的封装事件触发方法。通过从对象身上获取事件处理器(也就是收集之前通过接口或者EventTrigger设置的事件绑定组件),先判断是否有效,

        private static bool ShouldSendToComponent<T>(Component component) where T : IEventSystemHandler
        {
            var valid = component is T;
            if (!valid)
                return false;

            var behaviour = component as Behaviour;
            if (behaviour != null)
                return behaviour.isActiveAndEnabled;
            return true;
        }


如果有效,在游戏对象上冒泡指定的事件,找出哪个对象将实际接收该事件,也就是找到第一个符合要求的handler

        /// <summary>
        /// Whether the specified game object will be able to handle the specified event.
        /// </summary>
        public static bool CanHandleEvent<T>(GameObject go) where T : IEventSystemHandler
        {
            var internalHandlers = ListPool<IEventSystemHandler>.Get();
            GetEventList<T>(go, internalHandlers);
            var handlerCount = internalHandlers.Count;
            ListPool<IEventSystemHandler>.Release(internalHandlers);
            return handlerCount != 0;
        }

        /// <summary>
        /// Bubble the specified event on the game object, figuring out which object will actually receive the event.
        /// </summary>
        public static GameObject GetEventHandler<T>(GameObject root) where T : IEventSystemHandler
        {
            if (root == null)
                return null;

            Transform t = root.transform;
            while (t != null)
            {
                if (CanHandleEvent<T>(t.gameObject))
                    return t.gameObject;
                t = t.parent;
            }
            return null;
        }

执行 Execute方法然后根据事件类型对应到具体的事件类型的Execute方法,由于每帧获取handler很消耗性能,因此做了对象池逻辑来降低消耗。

        public static bool Execute<T>(GameObject target, BaseEventData eventData, EventFunction<T> functor) where T : IEventSystemHandler
        {
            var internalHandlers = ListPool<IEventSystemHandler>.Get();
            GetEventList<T>(target, internalHandlers);
            //  if (s_InternalHandlers.Count > 0)
            //      Debug.Log("Executinng " + typeof (T) + " on " + target);

            var internalHandlersCount = internalHandlers.Count;
            for (var i = 0; i < internalHandlersCount; i++)
            {
                T arg;
                try
                {
                    arg = (T)internalHandlers[i];
                }
                catch (Exception e)
                {
                    var temp = internalHandlers[i];
                    Debug.LogException(new Exception(string.Format("Type {0} expected {1} received.", typeof(T).Name, temp.GetType().Name), e));
                    continue;
                }

                try
                {
                    functor(arg, eventData);
                }
                catch (Exception e)
                {
                    Debug.LogException(e);
                }
            }

            var handlerCount = internalHandlers.Count;
            ListPool<IEventSystemHandler>.Release(internalHandlers);
            return handlerCount > 0;
        }
        /// <summary>
        /// Get the specified object's event event.
        /// </summary>
        private static void GetEventList<T>(GameObject go, IList<IEventSystemHandler> results) where T : IEventSystemHandler
        {
            // Debug.LogWarning("GetEventList<" + typeof(T).Name + ">");
            if (results == null)
                throw new ArgumentException("Results array is null", "results");

            if (go == null || !go.activeInHierarchy)
                return;

            var components = ListPool<Component>.Get();
            go.GetComponents(components);

            var componentsCount = components.Count;
            for (var i = 0; i < componentsCount; i++)
            {
                if (!ShouldSendToComponent<T>(components[i]))
                    continue;

                // Debug.Log(string.Format("{2} found! On {0}.{1}", go, s_GetComponentsScratch[i].GetType(), typeof(T)));
                results.Add(components[i] as IEventSystemHandler);
            }
            ListPool<Component>.Release(components);
            // Debug.LogWarning("end GetEventList<" + typeof(T).Name + ">");
        }

总结

这一篇介绍了事件系统的一些定义和消息的传递机制,对于整个触摸、点击事件的实现有了进一步的了解。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇