目前UGUI的ScrollRect不支持循环滑动,这里提供一种水平循环滑动的方法
简单描述一下思路:
(1)检测第一个物件的左边界和右边界,和Viewport的左边界比较。
(2)检测当前是向左滑动还是向右滑动
(3)修改Content的中心和边界,使Content的矩形随着具体内容的变化而变化
(4)左循环时把左边第一个元素放置到最右边
(5)右循环时把右边第一个元素放置到最左边。
直接放代码,使用方式和ScrollRect基本一致,注意设置枚举为HorizonLoop即可。
using System;
using UnityEngine.Events;
using UnityEngine.EventSystems;
namespace UnityEngine.UI
{
/// <summary>
/// Modified By HawkWang at 2017-04-07
/// All right not reserverd.
/// Copy and distribute and modify and delete at free will.
/// Too laze to support vertical loop scroll(and infact it is not as useful as horizon loop scroll).
/// </summary>
[AddComponentMenu("UI/Scroll Rect", 37)]
[SelectionBase]
[ExecuteInEditMode]
[DisallowMultipleComponent]
[RequireComponent(typeof(RectTransform))]
public class ScrollRectExt : UIBehaviour, IInitializePotentialDragHandler, IBeginDragHandler, IEndDragHandler, IDragHandler, IScrollHandler, ICanvasElement, ILayoutElement, ILayoutGroup
{
/// <summary>
/// Question One:研究哪里真正改变了Content的位置!
/// </summary>
#region ################ AddOn Content ##########################################
public enum EScrollType
{
HorizonLoop,
Horizon,
Vertical,
}
[SerializeField]
private EScrollType m_scrollType = EScrollType.HorizonLoop;
public enum EHorizonScrollDirection
{
Left,
Still,
Right,
}
EHorizonScrollDirection m_horizonDirection = EHorizonScrollDirection.Still;
public enum EMoveSiblingType
{
MoveFirstToLast,
MoveLastToFirst,
}
EMoveSiblingType m_moveSiblingType = EMoveSiblingType.MoveFirstToLast;
RectTransform FirstChild
{
get
{
return m_Content.GetChild(0) as RectTransform;
}
}
RectTransform LastChild
{
get
{
return m_Content.GetChild(m_Content.childCount - 1) as RectTransform;
}
}
float m_halfSize;
Vector2 m_viewPortWorldBound;//x 表示左边界,下边界,y表示右边界,上边界。
void InitExtContent()
{
if( horizontal && vertical == false )
{
HorizontalLayoutGroup hlg = m_Content.gameObject.GetComponent<HorizontalLayoutGroup>();
if( hlg != null )
{
m_halfSize = ( FirstChild.sizeDelta.x + hlg.spacing ) * 0.5f;
m_viewPortWorldBound = RectTransformUtility.WorldToScreenPoint(Camera.main, viewport.position);
m_viewPortWorldBound.y = m_viewPortWorldBound.x + viewport.sizeDelta.x;
}
}
if(vertical && horizontal == false)
{
VerticalLayoutGroup vlg = m_Content.gameObject.GetComponent<VerticalLayoutGroup>();
if( vlg != null )
{
//默认左上角对齐,所以下边界,采用的是x,上边界才是y
m_halfSize = (FirstChild.sizeDelta.y + vlg.spacing) * 0.5f;
m_viewPortWorldBound = RectTransformUtility.WorldToScreenPoint(Camera.main, viewport.position);
m_viewPortWorldBound.x = m_viewPortWorldBound.y - viewport.sizeDelta.y;
}
}
}
public bool horizontalLoop
{
get
{
return m_scrollType == EScrollType.HorizonLoop;
}
}
public bool horizontal
{
get
{
return m_scrollType == EScrollType.HorizonLoop || m_scrollType == EScrollType.Horizon;
}
}
public bool vertical
{
get
{
return m_scrollType == EScrollType.Vertical;
}
}
#endregion ########################################################################
public enum MovementType
{
Unrestricted, // Unrestricted movement -- can scroll forever
Elastic, // Restricted but flexible -- can go past the edges, but springs back in place
Clamped, // Restricted movement where it's not possible to go past the edges
}
public enum ScrollbarVisibility
{
Permanent,
AutoHide,
AutoHideAndExpandViewport,
}
[Serializable]
public class ScrollRectEvent : UnityEvent<Vector2> { }
[SerializeField]
private RectTransform m_Content;
public RectTransform content { get { return m_Content; } set { m_Content = value; } }
[SerializeField]
private MovementType m_MovementType = MovementType.Elastic;
public MovementType movementType { get { return m_MovementType; } set { m_MovementType = value; } }
[SerializeField]
private float m_Elasticity = 0.1f; // Only used for MovementType.Elastic
public float elasticity { get { return m_Elasticity; } set { m_Elasticity = value; } }
[SerializeField]
private bool m_Inertia = true;
public bool inertia { get { return m_Inertia; } set { m_Inertia = value; } }
[SerializeField]
private float m_DecelerationRate = 0.135f; // Only used when inertia is enabled
public float decelerationRate { get { return m_DecelerationRate; } set { m_DecelerationRate = value; } }
[SerializeField]
private float m_ScrollSensitivity = 1.0f;
public float scrollSensitivity { get { return m_ScrollSensitivity; } set { m_ScrollSensitivity = value; } }
[SerializeField]
private RectTransform m_Viewport;
public RectTransform viewport { get { return m_Viewport; } set { m_Viewport = value; SetDirtyCaching(); } }
[SerializeField]
private Scrollbar m_HorizontalScrollbar;
public Scrollbar horizontalScrollbar
{
get
{
return m_HorizontalScrollbar;
}
set
{
if (m_HorizontalScrollbar)
m_HorizontalScrollbar.onValueChanged.RemoveListener(SetHorizontalNormalizedPosition);
m_HorizontalScrollbar = value;
if (m_HorizontalScrollbar)
m_HorizontalScrollbar.onValueChanged.AddListener(SetHorizontalNormalizedPosition);
SetDirtyCaching();
}
}
[SerializeField]
private Scrollbar m_VerticalScrollbar;
public Scrollbar verticalScrollbar
{
get
{
return m_VerticalScrollbar;
}
set
{
if (m_VerticalScrollbar)
m_VerticalScrollbar.onValueChanged.RemoveListener(SetVerticalNormalizedPosition);
m_VerticalScrollbar = value;
if (m_VerticalScrollbar)
m_VerticalScrollbar.onValueChanged.AddListener(SetVerticalNormalizedPosition);
SetDirtyCaching();
}
}
[SerializeField]
private ScrollbarVisibility m_HorizontalScrollbarVisibility;
public ScrollbarVisibility horizontalScrollbarVisibility { get { return m_HorizontalScrollbarVisibility; } set { m_HorizontalScrollbarVisibility = value; SetDirtyCaching(); } }
[SerializeField]
private ScrollbarVisibility m_VerticalScrollbarVisibility;