UGUI—UIListView

作者:追风剑情 发布于:2019-11-4 17:56 分类:Unity3d

一、工程截图

222.png

3333.png

444.png

5555.png

二、代码实现

UIListView.cs


using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
/// <summary>
/// 自定义UIList
/// </summary>
public class UIListView : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    public RectTransform root;
    public RectTransform itemTemplet;
    public Rect rect;
    public int count;
    public float space; //间隔距离
    public float itemHeight;
    public float KickBackSpeed = 1;
    public float MoveSpeed = 1;
    private List<RectTransform> list;

    public delegate bool OnFillDataCallback(int index, GameObject go);
    public OnFillDataCallback OnFillData;

    private RectTransform m_RectTransform;
    private MoveDirection m_MoveDirection;
    private MoveState m_MoveState;
    private float m_TotalHeight;
    private float m_LastDragTime = 0;
    private Vector2 m_LastDragPosition;
    private float m_MoveY;
    private float t;

    public RectTransform rectTransform
    {
        get
        {
            if (ReferenceEquals(m_RectTransform, null))
            {
                m_RectTransform = GetComponent<RectTransform>();
            }
            return m_RectTransform;
        }
    }

    protected override void Start()
    {
        itemTemplet.gameObject.SetActive(false);
        float y = 0;
        for (int i=0; i<count; i++)
        {
            y = -(itemHeight + space) * i;
            RectTransform tran = CreateItem();
            SetPosition(tran, 0, y);
            //通过回调方法给选项设值
            if (OnFillData != null)
                OnFillData(i, tran.gameObject);
        }
        m_TotalHeight = Mathf.Abs(y) + itemHeight;

        RectMask2D mask = this.gameObject.AddComponent<RectMask2D>();
        mask.PerformClipping();
    }

    private void Update()
    {
        if (m_MoveState == MoveState.KickBack)
        {
            t += Time.deltaTime * KickBackSpeed;
            if (PositionY < 0)
                PositionY = Mathf.SmoothStep(PositionY, 0, t);
            else if (PositionY > MinPositionY)
                PositionY = Mathf.SmoothStep(PositionY, MinPositionY, t);
            else
                m_MoveState = MoveState.Static;
        } 
        else if (m_MoveState == MoveState.Moving)
        {
            t += Time.deltaTime * MoveSpeed;
            MoveSpeed /= 2;
            Debug.Log(MoveSpeed);
            PositionY = Mathf.SmoothStep(PositionY, m_MoveY, t);
            if (t >= 1 || MoveSpeed <= 0)
                m_MoveState = MoveState.Static;
        }
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        m_MoveState = MoveState.Static;
    }

    public void OnDrag(PointerEventData eventData)
    {
        Vector2 delta = eventData.delta;
        SetDeltaPosition(root, 0, delta.y);
        m_MoveDirection = delta.y > 0 ? MoveDirection.Up : MoveDirection.Down;
        m_MoveState = MoveState.Dragging;
        m_LastDragTime = Time.time;
        m_LastDragPosition = eventData.position;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        t = 0;
        m_MoveState = PositionY > MinPositionY || PositionY < 0 ? MoveState.KickBack : MoveState.Static;
        if (PositionY > MinPositionY || PositionY < 0)
        {
            m_MoveState = MoveState.KickBack;
        }
        else
        {
            if (Time.time - m_LastDragTime < 0.5f)
            {
                float time = Time.time - m_LastDragTime;
                float s = eventData.position.y - m_LastDragPosition.y;
                MoveSpeed = Mathf.Abs(s) / time / 1000;
                if (Mathf.Abs(s) > 1)
                {
                    m_MoveY = PositionY + s * 5000;
                    if (m_MoveY < 0)
                        m_MoveY = 0;
                    else if (m_MoveY > MinPositionY)
                        m_MoveY = MinPositionY;
                    m_MoveState = MoveState.Moving;
                    t = 0;
                }
                else
                {
                    m_MoveState = MoveState.Static;
                }
            }
            else
            {
                m_MoveState = MoveState.Static;
            }
        }
        
    }

    private RectTransform CreateItem()
    {
        RectTransform tran = GameObject.Instantiate<RectTransform>(itemTemplet);
        tran.SetParent(root);
        tran.gameObject.SetActive(true);
        return tran;
    }

    private void SetPosition(RectTransform tran, float x, float y)
    {
        tran.anchoredPosition = new Vector2(x, y);
    }

    private void SetDeltaPosition(RectTransform tran, float deltaX, float deltaY)
    {
        Vector2 pos = tran.anchoredPosition;
        pos.x += deltaX;
        pos.y += deltaY;
        tran.anchoredPosition = pos;
    }

    private float PositionY
    {
        get
        {
            return root.anchoredPosition.y;
        }
        set
        {
            SetPosition(root, 0, value);
        }
    }

    private float MinPositionY
    {
        get
        {
            if (m_TotalHeight <= rect.height)
                return 0;
            return m_TotalHeight - rect.height;
        }
    }

    private float BottomPositionY
    {
        get
        {
            return m_TotalHeight;
        }
    }

    private enum MoveDirection
    {
        None,
        Up,
        Down
    }

    private enum MoveState
    {
        Static,   //静止
        Dragging, //拖拽
        Moving,   //移动
        KickBack, //回弹
    }
}


UIListItem.cs


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
/// <summary>
/// 列表选项
/// </summary>
public class UIListItem : MonoBehaviour, IPointerClickHandler
{
    public Text text;
    public void SetText(string s)
    {
        this.text.text = s;
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        Debug.Log("Click: " + text.text);
    }
}


UIListViewTest.cs


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 测试
/// </summary>
public class UIListViewTest : MonoBehaviour
{
    void Awake()
    {
        UIListView listView = this.GetComponent<UIListView>();
        listView.OnFillData = new UIListView.OnFillDataCallback(OnFillData);
    }

    private bool OnFillData(int index, GameObject go)
    {
        Debug.LogFormat("OnFillData({0}, go)", index, go);
        UIListItem item = go.GetComponent<UIListItem>();
        item.SetText("Number " + index);

        return true;
    }
    
}


三、运行效果

11111.gif

第二版:支持无限循环、支持多行多列布局、支持左右或上下滑动

工程截图

11111.png

22222.png

3333333.png

44444.png

5555.png

UIListView.cs

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
/// <summary>
/// 无限循环列表
/// </summary>
public class UIListView : MonoBehaviour
{
    [SerializeField]
    private int m_Count = 0;
    private RectTransform m_RectTransform;
    private Rect m_ListRect;
    private UIListViewLayout m_Layout;
    private UIListViewDragHandler m_DragHandler;
    private bool m_started;
    //判断列表是否已初始化完成
    public bool initialized
    {
        get
        {
            return m_started;
        }
    }

    public RectTransform rectTransform
    {
        get {
            if (m_RectTransform == null)
                m_RectTransform = this.GetComponent<RectTransform>();
            return m_RectTransform;
        }
    }

    public Rect Rect { get { return rectTransform.rect; } }

    //数据个数
    public int Count { get { return m_Count; } set { m_Count = value; } }

    //列表宽度
    public float Width { get { return m_ListRect.width; } }

    //列表高度
    public float Height { get { return m_ListRect.height; } }

    private void Awake()
    {
        if (m_RectTransform == null)
            m_RectTransform = this.GetComponent<RectTransform>();
        m_ListRect = m_RectTransform.rect;
        Transform content = transform.Find("Viewport/Content");
        m_Layout = content.GetComponent<UIListViewLayout>();
        m_DragHandler = content.GetComponent<UIListViewDragHandler>();
    }

    private void Start()
    {
        m_started = true;
        //再次获取,确保获取正确值
        m_ListRect = m_RectTransform.rect;
    }

    //重置滑动 (会让列表坐标回到起始位置,并刷新数据)
    public void ResetScroll()
    {
        if (!initialized)
        {
            Debug.LogWarning("ListView initialization is not complete, Cannot call ResetScroll().");
            return;
        }
        m_DragHandler.ResetScroll();
        m_Layout.ResetLayout();
    }

    //刷新数据 (不影响列表坐标)
    public void Refresh()
    {
        if (!initialized)
        {
            Debug.LogWarning("ListView initialization is not complete, Cannot call Refresh().");
            return;
        }
        m_Layout.Refresh();
    }
}


IListViewItem.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 所有自定义的Item都要实现这个接口
/// </summary>
public interface IListViewItem
{
    int Index { get; set; }
    bool OnFillData(int index);
    GameObject gameObject { get; }
}


UIListViewLayout.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 控制列表布局
/// </summary>
public class UIListViewLayout : MonoBehaviour
{
    public enum LayoutDirection
    {
        Horizontal,
        Vertical
    }

    [SerializeField]
    private LayoutDirection m_direction = LayoutDirection.Vertical;
    public LayoutDirection layoutDirection { get { return m_direction; } }
    [SerializeField]
    private int m_RowCount = 1;
    [SerializeField]
    private int m_ColumnCount = 1;
    [SerializeField]
    private float m_RowSpace = 10;
    [SerializeField]
    private float m_ColumnSpace = 10;
    private UIListView m_ListView;
    private GameObject m_ItemTemplet;
    private Rect m_ListRect;
    private Rect m_ItemRect;
    private int m_InitCount = 0;
    private List<RectTransform> m_ItemList = new List<RectTransform>();

    private RectTransform m_RectTransform;
    public RectTransform rectTransform
    {
        get
        {
            if (m_RectTransform == null)
                m_RectTransform = this.GetComponent<RectTransform>();
            return m_RectTransform;
        }
    }

    public float Width
    {
        get
        {
            return rectTransform.rect.width;
        }
        set
        {
            Vector2 sizeDelta = rectTransform.sizeDelta;
            sizeDelta.x = value;
            rectTransform.sizeDelta = sizeDelta;
        }
    }

    public float Height
    {
        get
        {
            return rectTransform.rect.height;
        }
        set
        {
            Vector2 sizeDelta = rectTransform.sizeDelta;
            sizeDelta.y = value;
            rectTransform.sizeDelta = sizeDelta;
        }
    }



    private void Awake()
    {
        m_ListView = this.GetComponentInParent<UIListView>();
        Transform itemTemplet = transform.Find("ItemTemplet");
        if (itemTemplet != null)
        {
            m_ItemTemplet = itemTemplet.gameObject;
            m_ItemRect = m_ItemTemplet.GetComponent<RectTransform>().rect;
        }
        else
            Debug.LogErrorFormat("Not found ItemTemplet by Find(\"ItemTemplet\")");
    }

    private void Start()
    {
        m_ListRect = m_ListView.Rect;
        CalculateContentSize();
        InitItemList();
    }

    private void InitItemList()
    {
        //自动计算要初始生成多少个Item项
        if (m_direction == LayoutDirection.Vertical)
        {
            m_InitCount = Mathf.CeilToInt(m_ListRect.height / (m_ItemRect.height + m_RowSpace)) + 1;
            m_InitCount *= m_ColumnCount;
        }
        else
        {
            m_InitCount = Mathf.CeilToInt(m_ListRect.width / (m_ItemRect.width + m_ColumnSpace)) + 1;
            m_InitCount *= m_RowCount;
        }

        for (int i = 0; i < m_InitCount; i++)
        {
            GameObject go = CloneTemplet();
            IListViewItem item = go.GetComponent<IListViewItem>();
            item.Index = i;
            bool result = item.OnFillData(i);
            go.SetActive(result);
        }

        m_ItemTemplet.SetActive(false);
        UpdateLayout();
    }

    private GameObject CloneTemplet()
    {
        GameObject go = Instantiate(m_ItemTemplet);
        go.transform.SetParent(transform);
        go.transform.localScale = Vector3.one;
        go.transform.localPosition = Vector3.zero;
        m_ItemList.Add(go.GetComponent<RectTransform>());
        return go;
    }

    //计算内容区尺寸
    private void CalculateContentSize()
    {
        if (m_direction == LayoutDirection.Vertical)
        {
            int rowCount = m_ListView.Count / m_ColumnCount;
            if (m_ListView.Count % m_ColumnCount != 0)
                rowCount++;

            Width = m_ColumnCount * (m_ItemRect.width + m_ColumnSpace);
            float h = rowCount * (m_ItemRect.height + m_RowSpace);
            Height = Mathf.Max(h, m_ListView.Height);
        }
        else
        {
            int columnCount = m_ListView.Count / m_RowCount;
            if (m_ListView.Count % m_RowCount != 0)
                columnCount++;

            float w = columnCount * (m_ItemRect.width + m_ColumnSpace);
            Width = Mathf.Max(w, m_ListView.Width);
            Height = m_RowCount * (m_ItemRect.height + m_RowSpace);
        }
    }

    public void UpdateLayout()
    {
        for (int i = 0; i < m_ItemList.Count; i++)
        {
            IListViewItem item = m_ItemList[i].GetComponent<IListViewItem>();
            Vector3 pos = IndexToPosition(item.Index);
            m_ItemList[i].localPosition = pos;
        }
    }

    //将Content中的坐标转到Viewport坐标系中
    private Vector3 ContentToViewportPosition(Vector3 pos)
    {
        pos.x = rectTransform.anchoredPosition.x + pos.x;
        pos.y = rectTransform.anchoredPosition.y + pos.y;
        return pos;
    }

    //判断坐标是否在Viewport中
    private bool ContainViewportPosition(Vector3 pos)
    {
        bool inX = pos.x >= 0 && pos.x <= m_ListView.Width;
        bool inY = pos.y >= -m_ListView.Height && pos.y <= 0;
        return inX && inY;
    }

    //判断坐标是否在有效Content区中
    private bool ContainContentRangePosition(Vector3 pos)
    {
        bool inX = pos.x >= 0 && pos.x <= Width;
        bool inY = pos.y >= -Height && pos.y <= 0;
        return inX && inY;
    }

    //坐标转Index
    public int PositionToIndex(Vector3 pos)
    {
        int index = 0;
        float offsetY = (m_ItemRect.height + m_RowSpace) / 2;
        float offsetX = (m_ItemRect.width + m_ColumnSpace) / 2;
        float rowHeight = m_ItemRect.height + m_RowSpace;
        float columnWidth = m_ItemRect.width + m_ColumnSpace;
        int row = (int)((Mathf.Abs(pos.y) + offsetY) / rowHeight);
        int column = (int)((Mathf.Abs(pos.x) + offsetX) / columnWidth);
        if (m_direction == LayoutDirection.Vertical)
            index = row * m_ColumnCount + column + 1;
        else
            index = column * m_RowCount + row + 1;
        return index;
    }

    //Index转坐标
    public Vector3 IndexToPosition(int index)
    {
        Vector3 pos = Vector3.zero;
        int row, column;
        if (m_direction == LayoutDirection.Vertical)
        {
            row = index / m_ColumnCount;
            column = index % m_ColumnCount;
        }
        else
        {
            row = index % m_RowCount;
            column = index / m_RowCount;
        }
        float x = column * (m_ItemRect.width + m_ColumnSpace);
        float y = row * (m_ItemRect.height + m_RowSpace);
        pos.x = x;
        pos.y = -y;
        return pos;
    }

    //后移Item
    public void BackItem()
    {
        RectTransform firstTran = m_ItemList[0];
        RectTransform lastTran = m_ItemList[m_ItemList.Count - 1];
        Vector3 firstPos = firstTran.localPosition;
        Vector3 lastPos = lastTran.localPosition;
        //如果lastItem的左上角还在可视区内可见,则直接返回
        Vector3 ipos = lastPos;
        ipos = ContentToViewportPosition(ipos);
        if (ContainViewportPosition(ipos))
            return;
        //如果lastItem的右下角还在可视区内可见,则直接返回
        ipos.x += lastTran.rect.width;
        ipos.y -= lastTran.rect.height;
        if (ContainViewportPosition(ipos))
            return;

        IListViewItem firstItem = firstTran.GetComponent<IListViewItem>();
        int index = firstItem.Index - 1;
        //数据越界
        if (index < 0)
            return;
        m_ItemList.RemoveAt(m_ItemList.Count - 1);
        m_ItemList.Insert(0, lastTran);
        lastTran.localPosition = IndexToPosition(index);
        //填充数据
        IListViewItem lastItem = lastTran.GetComponent<IListViewItem>();
        lastItem.Index = index;
        bool result = lastItem.OnFillData(index);
        lastTran.gameObject.SetActive(result);
    }

    //前移Item
    public void ForwardItem()
    {
        RectTransform firstTran = m_ItemList[0];
        RectTransform lastTran = m_ItemList[m_ItemList.Count - 1];
        Vector3 firstPos = firstTran.localPosition;
        Vector3 lastPos = lastTran.localPosition;
        //如果firstItem的左上角还在可视区内可见,则直接返回
        Vector3 ipos = firstPos;
        ipos = ContentToViewportPosition(ipos);
        if (ContainViewportPosition(ipos))
            return;
        //如果firstItem的右下角还在可视区内可见,则直接返回
        //以下算法基于item的Pivot为左上角(0,1)
        ipos.x += firstTran.rect.width;
        ipos.y -= firstTran.rect.height;
        if (ContainViewportPosition(ipos))
            return;

        IListViewItem lastItem = lastTran.GetComponent<IListViewItem>();
        int index = lastItem.Index + 1;
        //数据越界检查
        if (m_direction == LayoutDirection.Vertical)
        {
            //确保整行往前移动
            bool isFirstColumn = (index % m_ColumnCount == 0);
            if (index >= m_ListView.Count && isFirstColumn)
                return;
        }
        else
        {
            //确保整列往前移动
            bool isFirstRow = (index % m_RowCount == 0);
            if (index >= m_ListView.Count && isFirstRow)
                return;
        }
        
        m_ItemList.RemoveAt(0);
        m_ItemList.Add(firstTran);
        firstTran.localPosition = IndexToPosition(index);
        //填充数据
        IListViewItem firstItem = firstTran.GetComponent<IListViewItem>();
        firstItem.Index = index;
        bool result = firstItem.OnFillData(index);
        firstTran.gameObject.SetActive(result);
    }

    //重置布局
    public void ResetLayout()
    {
        for (int i = 0; i < m_ItemList.Count; i++)
        {
            IListViewItem item = m_ItemList[i].GetComponent<IListViewItem>();
            item.Index = i;
            bool active = item.OnFillData(item.Index);
            item.gameObject.SetActive(active);
            item.gameObject.transform.localPosition = IndexToPosition(item.Index);
        }
        CalculateContentSize();
    }

    //刷新数据
    public void Refresh()
    {
        var firstItem = m_ItemList[0].GetComponent<IListViewItem>();
        int index = firstItem.Index;
        for (int i = 0; i < m_ItemList.Count; i++)
        {
            IListViewItem item = m_ItemList[i].GetComponent<IListViewItem>();
            bool active = item.OnFillData(index++);
            item.gameObject.SetActive(active);
        }
        CalculateContentSize();
    }
}


UIListViewDragHandler.cs

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
/// <summary>
/// 挂在Content上
/// </summary>
public class UIListViewDragHandler : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerDownHandler
{
    public float MoveSpeed = 1;
    private RectTransform m_RectTransform;
    private MoveDirection m_MoveDirection;
    private MoveState m_MoveState;
    private float m_LastDragTime;
    private Vector2 m_LastDragPosition;
    private float t;
    private float m_MoveY;
    private float m_MoveX;

    private UIListView m_UIListView;
    private UIListViewLayout m_Layout;
    private Rect m_ListRect;
    private const float MAX_SPEED = 1f;

    [Serializable]
    public class OnScrollEndEvent : UnityEvent { }
    // 防止序列化变量重命名后丢失引用
    [FormerlySerializedAs("onScrollEnd")]
    [SerializeField]
    private OnScrollEndEvent m_OnScrollEnd = new OnScrollEndEvent();

    private enum MoveDirection
    {
        None,
        Up,
        Down,
        Left,
        Right
    }

    private enum MoveState
    {
        Static,   //静止
        Dragging, //拖拽
        Moving,   //移动
        KickBack, //回弹
    }

    private void Start()
    {
        m_RectTransform = this.GetComponent<RectTransform>();
        m_Layout = this.GetComponent<UIListViewLayout>();
        m_UIListView = transform.GetComponentInParent<UIListView>();
        m_ListRect = m_UIListView.Rect;
    }

    private void Update()
    {
        switch (m_MoveState)
        {
            case MoveState.Moving:
                t += Time.deltaTime * MoveSpeed;
                MoveSpeed /= 2;
                switch (m_Layout.layoutDirection)
                {
                    case UIListViewLayout.LayoutDirection.Vertical:
                        PositionY = Mathf.SmoothStep(PositionY, m_MoveY, t);
                        break;
                    case UIListViewLayout.LayoutDirection.Horizontal:
                        PositionX = Mathf.SmoothStep(PositionX, m_MoveX, t);
                        break;
                }
                if (t >= 1 || MoveSpeed <= 0)
                    m_MoveState = MoveState.Static;

                switch (m_MoveDirection)
                {
                    case MoveDirection.Up:
                    case MoveDirection.Left:
                        m_Layout.ForwardItem();
                        break;
                    case MoveDirection.Down:
                    case MoveDirection.Right:
                        m_Layout.BackItem();
                        break;
                }
                break;
            case MoveState.KickBack:
                t += Time.deltaTime * MoveSpeed;
                switch (m_Layout.layoutDirection)
                {
                    case UIListViewLayout.LayoutDirection.Vertical:
                        m_MoveDirection = PositionY > m_MoveY ? MoveDirection.Down : MoveDirection.Up;
                        PositionY = Mathf.SmoothStep(PositionY, m_MoveY, t);
                        break;
                    case UIListViewLayout.LayoutDirection.Horizontal:
                        m_MoveDirection = PositionY > m_MoveY ? MoveDirection.Right : MoveDirection.Left;
                        PositionX = Mathf.SmoothStep(PositionX, m_MoveX, t);
                        break;
                }
                if (t >= 1)
                    m_MoveState = MoveState.Static;

                switch (m_MoveDirection)
                {
                    case MoveDirection.Up:
                    case MoveDirection.Left:
                        m_Layout.ForwardItem();
                        break;
                    case MoveDirection.Down:
                    case MoveDirection.Right:
                        m_Layout.BackItem();
                        break;
                }
                break;
        }
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        if (m_MoveState == MoveState.Moving)
            m_MoveState = MoveState.Static;
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        m_MoveState = MoveState.Static;
    }

    public void OnDrag(PointerEventData eventData)
    {
        m_MoveState = MoveState.Dragging;
        m_LastDragTime = Time.time;
        m_LastDragPosition = eventData.position;
        Vector2 delta = eventData.delta;
        switch (m_Layout.layoutDirection)
        {
            case UIListViewLayout.LayoutDirection.Vertical:
                m_MoveDirection = delta.y > 0 ? MoveDirection.Up : MoveDirection.Down;
                SetDeltaPositionY(delta.y);
                break;
            case UIListViewLayout.LayoutDirection.Horizontal:
                m_MoveDirection = delta.x > 0 ? MoveDirection.Right : MoveDirection.Left;
                SetDeltaPositionX(delta.x);
                break;
        }

        switch (m_MoveDirection)
        {
            case MoveDirection.Up:
            case MoveDirection.Left:
                m_Layout.ForwardItem();
                break;
            case MoveDirection.Down:
            case MoveDirection.Right:
                m_Layout.BackItem();
                break;
        }
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        t = 0;
        CheckKickBack();
        if (m_MoveState == MoveState.KickBack)
            return;

        switch (m_Layout.layoutDirection)
        {
            case UIListViewLayout.LayoutDirection.Vertical:
                if (Time.time - m_LastDragTime < 0.5f)
                {
                    float time = Time.time - m_LastDragTime;
                    float s = eventData.position.y - m_LastDragPosition.y;
                    MoveSpeed = Mathf.Abs(s) / time / 5000;
                    MoveSpeed = Mathf.Clamp(MoveSpeed, 0, MAX_SPEED);
                    if (Mathf.Abs(s) > 1)
                    {
                        m_MoveY = PositionY + s * 5000;
                        if (m_MoveY < MinPositionY)
                            m_MoveY = MinPositionY;
                        else if (m_MoveY > MaxPositionY)
                            m_MoveY = MaxPositionY;
                        m_MoveState = MoveState.Moving;
                    }
                    else
                    {
                        m_MoveState = MoveState.Static;
                    }
                }
                else
                {
                    m_MoveState = MoveState.Static;
                }
                break;
            case UIListViewLayout.LayoutDirection.Horizontal:
                if (Time.time - m_LastDragTime < 0.5f)
                {
                    float time = Time.time - m_LastDragTime;
                    float s = eventData.position.x - m_LastDragPosition.x;
                    MoveSpeed = Mathf.Abs(s) / time / 5000;
                    MoveSpeed = Mathf.Clamp(MoveSpeed, 0, MAX_SPEED);
                    if (Mathf.Abs(s) > 1)
                    {
                        m_MoveX = PositionX + s * 5000;
                        if (m_MoveX < MinPositionX)
                            m_MoveX = MinPositionX;
                        else if (m_MoveX > MaxPositionX)
                            m_MoveX = MaxPositionX;
                        m_MoveState = MoveState.Moving;
                    }
                    else
                    {
                        m_MoveState = MoveState.Static;
                    }
                }
                else
                {
                    m_MoveState = MoveState.Static;
                }
                break;
        }
    }

    //检测是否进入回弹状态
    private void CheckKickBack()
    {
        switch (m_Layout.layoutDirection)
        {
            case UIListViewLayout.LayoutDirection.Vertical:
                if (PositionY < MinPositionY || PositionY > MaxPositionY)
                {
                    m_MoveState = MoveState.KickBack;
                    MoveSpeed = 2;
                    m_MoveY = Mathf.Clamp(PositionY, MinPositionY, MaxPositionY);
                }

                if (PositionY >= MaxPositionY)
                    m_OnScrollEnd.Invoke();
                break;
            case UIListViewLayout.LayoutDirection.Horizontal:
                if (PositionX < MinPositionX || PositionX > MaxPositionX)
                {
                    m_MoveState = MoveState.KickBack;
                    MoveSpeed = 2;
                    m_MoveX = Mathf.Clamp(PositionX, MinPositionX, MaxPositionX);
                }

                if (PositionX <= MinPositionX)
                    m_OnScrollEnd.Invoke();
                break;
        }
    }

    private void SetDeltaPosition(float deltaX, float deltaY)
    {
        Vector2 pos = m_RectTransform.anchoredPosition;
        pos.x += deltaX;
        pos.y += deltaY;
        m_RectTransform.anchoredPosition = pos;
    }

    private void SetDeltaPositionX(float deltaX)
    {
        Vector2 pos = m_RectTransform.anchoredPosition;
        pos.x += deltaX;
        m_RectTransform.anchoredPosition = pos;
    }

    private void SetDeltaPositionY(float deltaY)
    {
        Vector2 pos = m_RectTransform.anchoredPosition;
        pos.y += deltaY;
        m_RectTransform.anchoredPosition = pos;
    }

    private void SetPosition(float x, float y)
    {
        Vector2 pos = m_RectTransform.anchoredPosition;
        pos.x = x;
        pos.y = y;
        m_RectTransform.anchoredPosition = pos;
    }

    private float PositionX
    {
        get {
            return m_RectTransform.anchoredPosition.x;
        }
        set {
            SetPosition(value, m_RectTransform.anchoredPosition.y);
        }
    }

    private float PositionY
    {
        get {
            return m_RectTransform.anchoredPosition.y;
        }
        set {
            SetPosition(m_RectTransform.anchoredPosition.x, value);
        }
    }

    private float MinPositionX 
    { 
        get { return m_UIListView.Width - m_Layout.Width; } 
    }

    private float MaxPositionX
    {
        get { return 0; }
    }

    private float MinPositionY
    {
        get { return 0; }
    }

    private float MaxPositionY
    {
        get { return m_Layout.Height - m_UIListView.Height; }
    }

    //重置滑动
    public void ResetScroll()
    {
        m_MoveState = MoveState.Static;
        SetPosition(0, 0);
    }
}


SimpleListViewItem.cs


using UnityEngine.EventSystems;
// 简单实现 IListViewItem
public class SimpleListViewItem : GameEventBehaviour, IListViewItem, IPointerDownHandler, IPointerUpHandler, IPointerClickHandler
{
    public virtual int Index { get; set; }
    public virtual bool OnFillData(int index)
    {
        return true;
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        OnPointerDown();
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        OnPointerUp();
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        OnPointerClick();
    }

    public virtual void OnPointerDown() { }
    public virtual void OnPointerUp() { }
    public virtual void OnPointerClick() { }
}


CustomListItem.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 自定义Item类
/// </summary>
public class CustomListItem : MonoBehaviour, IListViewItem
{
    [SerializeField]
    private Text m_Text;

    public int Index { get; set; }

    public bool OnFillData(int index)
    {
        m_Text.text = string.Format("Item {0}", index);
        return true;
    }

    public void OnClick()
    {
        Debug.Log(m_Text.text);
    }
}

运行测试
上下滑动
3339.gif
左右滑动
3340.gif

标签: Unity3d

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号