UIGU源码分析8:Slider

黄扬
2023-12-01

源码8:Slider

  public class Slider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement
    {
    	...
    	
    	  [Serializable]
        /// <summary>
        /// Event type used by the UI.Slider.
        /// </summary>
        public class SliderEvent : UnityEvent<float> {}

        [SerializeField]
        private RectTransform m_FillRect;
        
          [SerializeField]
        private RectTransform m_HandleRect;
        
            [SerializeField]
        private Direction m_Direction = Direction.LeftToRight;
        
          [SerializeField]
        private float m_MinValue = 0;
        
        [SerializeField]
        private float m_MaxValue = 1;
        
            [SerializeField]
        private bool m_WholeNumbers = false;
        
         [SerializeField]
        protected float m_Value;
        
          public SliderEvent onValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } }
    	...
    
    }

Slider 继承了Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement 功能基本和Scrollbar类似


   protected virtual void Set(float input, bool sendCallback = true)
    {
        // Clamp the input
        float newValue = ClampValue(input);

        // If the stepped value doesn't match the last one, it's time to update
        if (m_Value == newValue)
            return;

        m_Value = newValue;
        UpdateVisuals();
        if (sendCallback)
        {
            UISystemProfilerApi.AddMarker("Slider.value", this);
            m_OnValueChanged.Invoke(newValue);
        }
    }

Set方法不要是设置Slider的值 通过ClampValue 限定m_Value 得值在minValue和maxValue之间 如果Value值改变 就会调用UpdateVisuals来更新Slider得显示 同时会发送m_OnValueChanged事件

        private void UpdateVisuals()
        {
#if UNITY_EDITOR
            if (!Application.isPlaying)
                UpdateCachedReferences();
#endif

            m_Tracker.Clear();

            if (m_FillContainerRect != null)
            {
                m_Tracker.Add(this, m_FillRect, DrivenTransformProperties.Anchors);
                Vector2 anchorMin = Vector2.zero;
                Vector2 anchorMax = Vector2.one;

                if (m_FillImage != null && m_FillImage.type == Image.Type.Filled)
                {
                    m_FillImage.fillAmount = normalizedValue;
                }
                else
                {
                    if (reverseValue)
                        anchorMin[(int)axis] = 1 - normalizedValue;
                    else
                        anchorMax[(int)axis] = normalizedValue;
                }

                m_FillRect.anchorMin = anchorMin;
                m_FillRect.anchorMax = anchorMax;
            }

            if (m_HandleContainerRect != null)
            {
                m_Tracker.Add(this, m_HandleRect, DrivenTransformProperties.Anchors);
                Vector2 anchorMin = Vector2.zero;
                Vector2 anchorMax = Vector2.one;
                anchorMin[(int)axis] = anchorMax[(int)axis] = (reverseValue ? (1 - normalizedValue) : normalizedValue);
                m_HandleRect.anchorMin = anchorMin;
                m_HandleRect.anchorMax = anchorMax;
            }
        }

UpdateVisuals 和Scrollbar里的功能类似。

当m_FillImage.type == Image.Type.Filled 时 直接更改m_FillImage的 fillAmount

其他类型的时候时更改锚点 从而达更新进度条和划块的位置


    public override void OnPointerDown(PointerEventData eventData)
    {
        if (!MayDrag(eventData))
            return;

        base.OnPointerDown(eventData);

        m_Offset = Vector2.zero;
        if (m_HandleContainerRect != null && RectTransformUtility.RectangleContainsScreenPoint(m_HandleRect, eventData.pointerPressRaycast.screenPosition, eventData.enterEventCamera))
        {
            Vector2 localMousePos;
            if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HandleRect, eventData.pointerPressRaycast.screenPosition, eventData.pressEventCamera, out localMousePos))
                m_Offset = localMousePos;
        }
        else
        {
            // Outside the slider handle - jump to this point instead
            UpdateDrag(eventData, eventData.pressEventCamera);
        }
    }

重写了Selectable中的OnPointerDown 方法

当按下的时候 判断是否在m_HandleRect中,如果在里面的话就记录选中位置 相对m_HandleRect 的偏移量(移动的时候减去这个偏移量就能防止m_HandleRect跳动)

如果不存在的话就执行UpdateDrag 让滑条和划块设置到当前点对应的值(最终还是调用到上面的Set方法)

  void UpdateDrag(PointerEventData eventData, Camera cam)
    {
        RectTransform clickRect = m_HandleContainerRect ?? m_FillContainerRect;
        if (clickRect != null && clickRect.rect.size[(int)axis] > 0)
        {
            Vector2 position = Vector2.zero;
            if (!MultipleDisplayUtilities.GetRelativeMousePositionForDrag(eventData, ref position))
                return;

            Vector2 localCursor;
            if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, position, cam, out localCursor))
                return;
            localCursor -= clickRect.rect.position;

            float val = Mathf.Clamp01((localCursor - m_Offset)[(int)axis] / clickRect.rect.size[(int)axis]);
            normalizedValue = (reverseValue ? 1f - val : val);

重写了Selectable中的OnMove方法 主要作用就是当方向键按下并与ScrollBar的方向一致时,便不在导航到下一个Selectable(和Scrollbar一致)

    public override void OnMove(AxisEventData eventData)
    {
        if (!IsActive() || !IsInteractable())
        {
            base.OnMove(eventData);
            return;
        }

        switch (eventData.moveDir)
        {
            case MoveDirection.Left:
                if (axis == Axis.Horizontal && FindSelectableOnLeft() == null)
                    Set(reverseValue ? value + stepSize : value - stepSize);
                else
                    base.OnMove(eventData);
                break;
            case MoveDirection.Right:
                if (axis == Axis.Horizontal && FindSelectableOnRight() == null)
                    Set(reverseValue ? value - stepSize : value + stepSize);
                else
                    base.OnMove(eventData);
                break;
            case MoveDirection.Up:
                if (axis == Axis.Vertical && FindSelectableOnUp() == null)
                    Set(reverseValue ? value - stepSize : value + stepSize);
                else
                    base.OnMove(eventData);
                break;
            case MoveDirection.Down:
                if (axis == Axis.Vertical && FindSelectableOnDown() == null)
                    Set(reverseValue ? value + stepSize : value - stepSize);
                else
                    base.OnMove(eventData);
                break;

实现了OnDrag方法 主要是为了在滑动中调用UpdateDrag,直接设置对应的值

  public virtual void OnDrag(PointerEventData eventData)
    {
        if (!MayDrag(eventData))
            return;
        UpdateDrag(eventData, eventData.pressEventCamera);
    }
 类似资料: