双重滑动列表比较重要的点就是在于父子列表的滑动事件传递,不然在上的Scroll Rect会覆盖下层的,导致下层事件不响应。其次就是滑动后的页数位置判断,将UI能自动滑到对应的页数。
首先来看最重要的事件传递,由于Scroll Rect没有自带可使用的滑动事件,于是我们就直接注册Unity的事件接口即可:
IBeginDragHandler, 鼠标按下时触发,
IDragHandler, 鼠标保持按下状态时会一直触发
IEndDragHandler 结束鼠标拖动,松开的时候会触发一次
这里有个坑注意一下,即使你只需要用到其中一个事件,也必须要有IDragHandle,不然不会触发滑动事件,具体代码实现如下:
public void OnBeginDrag(PointerEventData eventData)
{
PointerEventData pointerEventData = (PointerEventData)eventData;
if (_thisScroll.horizontal)
{
float angle = Mathf.Acos(Vector2.Dot(Vector2.up.normalized, pointerEventData.delta.normalized)) * Mathf.Rad2Deg;
if (angle < 45f || angle > 135f)
{
pointerEventData.pointerDrag = _outsideScroll.gameObject;
_outsideScroll.OnBeginDrag(pointerEventData);
}
else
{
pointerEventData.pointerDrag = _thisScroll.gameObject;
_thisScroll.OnBeginDrag(pointerEventData);
}
}
else if (_thisScroll.vertical)
{
float angle = Mathf.Acos(Vector2.Dot(Vector2.right.normalized, pointerEventData.delta.normalized)) * Mathf.Rad2Deg;
if (angle < 45f || angle > 135f)
{
pointerEventData.pointerDrag = _outsideScroll.gameObject;
_outsideScroll.OnBeginDrag(pointerEventData);
}
else
{
pointerEventData.pointerDrag = _thisScroll.gameObject;
_thisScroll.OnBeginDrag(pointerEventData);
}
}
else
{
pointerEventData.pointerDrag = _outsideScroll.gameObject;
_outsideScroll.OnBeginDrag(pointerEventData);
}
}
public void OnEndDrag(PointerEventData eventData)
{
PointerEventData pointerEventData = (PointerEventData)eventData;
if (pointerEventData.pointerDrag == _outsideScroll.gameObject)
{
_outsideScroll.OnEndDrag(eventData);
}
else
{
_thisScroll.OnEndDrag(eventData);
}
}
public void OnDrag(PointerEventData eventData)
{
PointerEventData pointerEventData = (PointerEventData)eventData;
if (pointerEventData.pointerDrag == _outsideScroll.gameObject)
{
_outsideScroll.OnDrag(eventData);
}
else
{
_thisScroll.OnDrag(eventData);
}
}
主要逻辑就是通过开始拖拽事件判断角度,从而判断是横向滑动还是竖向滑动,并通向正确的Scroll Rect滑动事件。
然后外层挂载的滑动列表事件具体如下,主要是用来判断显示的页数,并自动滑到那一页。
private ScrollRect rect;
private List<float> posList = new List<float>();
private int curIndex = -1;
public float speed = 4;
private bool _isDragging = false;
public Action<int> onChildSelected = null;
private void Awake()
{
ReferenceCollector referenceCollector = gameObject.GetComponent<ReferenceCollector>();
rect = GetComponent<ScrollRect>();
for (int i = 0; i < rect.content.transform.childCount; i++)
{
posList.Add(i * (1.0f / (float)(rect.content.transform.childCount - 1)));
}
}
private void Start()
{
PageTo(0);
}
private void Update()
{
if (curIndex >= 0 && !_isDragging)
{
rect.horizontalNormalizedPosition = Mathf.Lerp(rect.horizontalNormalizedPosition,
posList[curIndex], Time.deltaTime * speed);
}
}
public void OnBeginDrag(PointerEventData eventData)
{
_isDragging = true;
rect.OnBeginDrag(eventData);
}
public void OnEndDrag(PointerEventData eventData)
{
rect.OnEndDrag(eventData);
_isDragging = false;
int index = 0;
for (int i = 1; i < posList.Count; i++)
{
if (Mathf.Abs(posList[i] - rect.horizontalNormalizedPosition) <
Mathf.Abs(posList[index] - rect.horizontalNormalizedPosition))
{
index = i;
}
}
PageTo(index);
}
public void OnDrag(PointerEventData eventData)
{
rect.OnDrag(eventData);
}
public void PageTo(int index)
{
if (curIndex != index)
{
curIndex = index;
if (onChildSelected != null)
{
onChildSelected(curIndex);
}
}
}
}