UGUI内核大探究(十一)ScrollRect与ScrollBar

当我们在Unity Editor里创建一个Scroll View的时候含有ScrollRect的对象,它下面还有三个子对象,两个含有ScrollBar组件的子对象是作为滚动条,一个Viewport用于限定显示区域。我们可以为Viewport下面的Content对象添加组件(例如Image)或者子对象。点击运行,我们就可以拖动Scroll View,并且看到里面的内容也跟着在滚动。本文就详细分析一下ScrollRect和ScrollBar的源码,了解一下它们是怎么动起来的。

按照惯例,附上UGUI源码下载地址

首先介绍ScrollBar,它继承自Selectable,还继承了IBeginDragHandler, IDragHandler, IInitializePotentialDragHandler, ICanvasElement四个接口。


ScrollBar重写了OnEnable和OnDisable(调用时机参见Untiy3D组件小贴士(一)OnEnabled与OnDisabled)方法。OnEnable里会找到m_HandleRect父对象的RectTransform组件作为m_ContainerRect。如果所示:


1号对应的是ScrollBar,2号对应的是m_ContainerRect,3号对应的是m_HandleRect。

然后OnEnable会重新设置当前值(value),并刷新表现,即根据当前Value设置m_HandleRect的anchorMin和anchorMax,体现出来就是滚动条的位置发生了变化

OnDisable只是调用DrivenRectTransformTracker类型的m_Tracker的Clear方法。(参考https://docs.unity3d.com/462/Documentation/ScriptReference/DrivenRectTransformTracker.html。)

ScrollBar还重写了Selectable的OnPointerDown方法,设置isPointerDownAndNotDragging为true,使用协程调用了ClickRepeat方法,判断点击事件是否在m_HandleRect外面(一定在Scrollbar里面),如果在外面,就将事件坐标转换到m_HandleRect的本地坐标系里,然后调整value,直到点击事件在m_HandleRect里面。

重写的OnPointerUp方法里面,设置isPointerDownAndNotDragging为false。

另外还重写了Selectable的OnMove、FindSelectableOnLeft、FindSelectableOnRight、FindSelectableOnUp和FindSelectableOnDown方法。当方向键按下并与ScrollBar的方向一致时,便不在导航到下一个Selectable,而是修改value值(加减stepSize),即移动滚动条。

OnBeginDrag是继承自IBeginDragHandler接口的方法,这个方法里记录了拖拽的起始点(m_HandleRect内相对center的坐标)。

OnDrag是继承自IDragHandler接口的方法,这个方法会调用UpdateDrag方法。

        void UpdateDrag(PointerEventData eventData)
        {
            if (eventData.button != PointerEventData.InputButton.Left)
                return;

            if (m_ContainerRect == null)
                return;

            Vector2 localCursor;
            if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(m_ContainerRect, eventData.position, eventData.pressEventCamera, out localCursor))
                return;

            Vector2 handleCenterRelativeToContainerCorner = localCursor - m_Offset - m_ContainerRect.rect.position;
            Vector2 handleCorner = handleCenterRelativeToContainerCorner - (m_HandleRect.rect.size - m_HandleRect.sizeDelta) * 0.5f;

            float parentSize = axis == 0 ? m_ContainerRect.rect.width : m_ContainerRect.rect.height;
            float remainingSize = parentSize * (1 - size);
            if (remainingSize <= 0)
                return;

            switch (m_Direction)
            {
                case Direction.LeftToRight:
                    Set(handleCorner.x / remainingSize);
                    break;
                case Direction.RightToLeft:
                    Set(1f - (handleCorner.x / remainingSize)
  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值