UGUI—UIListView
作者:追风剑情 发布于:2019-11-4 17:56 分类:Unity3d
一、工程截图
二、代码实现
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; } }
三、运行效果
第二版:支持无限循环、支持多行多列布局、支持左右或上下滑动
工程截图
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); } }
标签: Unity3d
日历
最新文章
随机文章
热门文章
分类
存档
- 2024年11月(3)
- 2024年10月(5)
- 2024年9月(3)
- 2024年8月(3)
- 2024年7月(11)
- 2024年6月(3)
- 2024年5月(9)
- 2024年4月(10)
- 2024年3月(11)
- 2024年2月(24)
- 2024年1月(12)
- 2023年12月(3)
- 2023年11月(9)
- 2023年10月(7)
- 2023年9月(2)
- 2023年8月(7)
- 2023年7月(9)
- 2023年6月(6)
- 2023年5月(7)
- 2023年4月(11)
- 2023年3月(6)
- 2023年2月(11)
- 2023年1月(8)
- 2022年12月(2)
- 2022年11月(4)
- 2022年10月(10)
- 2022年9月(2)
- 2022年8月(13)
- 2022年7月(7)
- 2022年6月(11)
- 2022年5月(18)
- 2022年4月(29)
- 2022年3月(5)
- 2022年2月(6)
- 2022年1月(8)
- 2021年12月(5)
- 2021年11月(3)
- 2021年10月(4)
- 2021年9月(9)
- 2021年8月(14)
- 2021年7月(8)
- 2021年6月(5)
- 2021年5月(2)
- 2021年4月(3)
- 2021年3月(7)
- 2021年2月(2)
- 2021年1月(8)
- 2020年12月(7)
- 2020年11月(2)
- 2020年10月(6)
- 2020年9月(9)
- 2020年8月(10)
- 2020年7月(9)
- 2020年6月(18)
- 2020年5月(4)
- 2020年4月(25)
- 2020年3月(38)
- 2020年1月(21)
- 2019年12月(13)
- 2019年11月(29)
- 2019年10月(44)
- 2019年9月(17)
- 2019年8月(18)
- 2019年7月(25)
- 2019年6月(25)
- 2019年5月(17)
- 2019年4月(10)
- 2019年3月(36)
- 2019年2月(35)
- 2019年1月(28)
- 2018年12月(30)
- 2018年11月(22)
- 2018年10月(4)
- 2018年9月(7)
- 2018年8月(13)
- 2018年7月(13)
- 2018年6月(6)
- 2018年5月(5)
- 2018年4月(13)
- 2018年3月(5)
- 2018年2月(3)
- 2018年1月(8)
- 2017年12月(35)
- 2017年11月(17)
- 2017年10月(16)
- 2017年9月(17)
- 2017年8月(20)
- 2017年7月(34)
- 2017年6月(17)
- 2017年5月(15)
- 2017年4月(32)
- 2017年3月(8)
- 2017年2月(2)
- 2017年1月(5)
- 2016年12月(14)
- 2016年11月(26)
- 2016年10月(12)
- 2016年9月(25)
- 2016年8月(32)
- 2016年7月(14)
- 2016年6月(21)
- 2016年5月(17)
- 2016年4月(13)
- 2016年3月(8)
- 2016年2月(8)
- 2016年1月(18)
- 2015年12月(13)
- 2015年11月(15)
- 2015年10月(12)
- 2015年9月(18)
- 2015年8月(21)
- 2015年7月(35)
- 2015年6月(13)
- 2015年5月(9)
- 2015年4月(4)
- 2015年3月(5)
- 2015年2月(4)
- 2015年1月(13)
- 2014年12月(7)
- 2014年11月(5)
- 2014年10月(4)
- 2014年9月(8)
- 2014年8月(16)
- 2014年7月(26)
- 2014年6月(22)
- 2014年5月(28)
- 2014年4月(15)
友情链接
- Unity官网
- Unity圣典
- Unity在线手册
- Unity中文手册(圣典)
- Unity官方中文论坛
- Unity游戏蛮牛用户文档
- Unity下载存档
- Unity引擎源码下载
- Unity服务
- Unity Ads
- wiki.unity3d
- Visual Studio Code官网
- SenseAR开发文档
- MSDN
- C# 参考
- C# 编程指南
- .NET Framework类库
- .NET 文档
- .NET 开发
- WPF官方文档
- uLua
- xLua
- SharpZipLib
- Protobuf-net
- Protobuf.js
- OpenSSL
- OPEN CASCADE
- JSON
- MessagePack
- C在线工具
- 游戏蛮牛
- GreenVPN
- 聚合数据
- 热云
- 融云
- 腾讯云
- 腾讯开放平台
- 腾讯游戏服务
- 腾讯游戏开发者平台
- 腾讯课堂
- 微信开放平台
- 腾讯实时音视频
- 腾讯即时通信IM
- 微信公众平台技术文档
- 白鹭引擎官网
- 白鹭引擎开放平台
- 白鹭引擎开发文档
- FairyGUI编辑器
- PureMVC-TypeScript
- 讯飞开放平台
- 亲加通讯云
- Cygwin
- Mono开发者联盟
- Scut游戏服务器引擎
- KBEngine游戏服务器引擎
- Photon游戏服务器引擎
- 码云
- SharpSvn
- 腾讯bugly
- 4399原创平台
- 开源中国
- Firebase
- Firebase-Admob-Unity
- google-services-unity
- Firebase SDK for Unity
- Google-Firebase-SDK
- AppsFlyer SDK
- android-repository
- CQASO
- Facebook开发者平台
- gradle下载
- GradleBuildTool下载
- Android Developers
- Google中国开发者
- AndroidDevTools
- Android社区
- Android开发工具
- Google Play Games Services
- Google商店
- Google APIs for Android
- 金钱豹VPN
- TouchSense SDK
- MakeHuman
- Online RSA Key Converter
- Windows UWP应用
- Visual Studio For Unity
- Open CASCADE Technology
- 慕课网
- 阿里云服务器ECS
- 在线免费文字转语音系统
- AI Studio
- 网云穿
- 百度网盘开放平台
- 迅捷画图
- 菜鸟工具
- [CSDN] 程序员研修院
- 华为人脸识别
- 百度AR导航导览SDK
- 海康威视官网
- 海康开放平台
- 海康SDK下载
- git download
交流QQ群
-
Flash游戏设计: 86184192
Unity游戏设计: 171855449
游戏设计订阅号