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);
}