Skip to content

循环列表

简介

说明

在游戏开发中,我们经常需要展示大量的列表数据,比如:

  • 背包系统中成百上千的道具
  • 排行榜中大量的玩家数据
  • 聊天系统中的消息记录
  • 商城中的商品列表

如果为每一条数据都创建一个UI对象,不仅会占用大量内存,还会导致严重的性能问题。循环列表通过复用少量UI对象来展示大量数据,只创建可视区域内的对象,并在滚动时动态更新内容。这种机制可以大大降低内存占用和提升性能,是游戏开发中处理大量列表数据的最佳实践。

核心特性

  • 对象重用: 只创建可视区域所需的项目
  • 自动回收: 超出可视区域的项目自动回收
  • 性能优化: 最小化内存占用和渲染开销
  • 灵活配置: 支持垂直和水平方向的滚动、网格布局

构建UI界面代码使用演示

视频教程

以下视频展示了UI框架生成通用UI组件代码的使用流程

视频内操作介绍

  1. 循环列表的创建:可以看到视频内创建了一个Horizontal类型的循环列表,命名为:ELoopScrollList_Test
  2. 循环列表的Item创建:同时也创建了一个名为:Item_Test的物体,放在了Assets/Bundles/UI/Item文件夹下,同时跟Common一样为生成其对应的代码文件
  3. 循环列表指定Item:同时在循环列表ELoopScrollList_Test身上的Loop Horizontal Scroll Rect组件指定了一下当前循环列表要用的Item预制体名称,后续会通过YooAsset资源管理去加载对应的Item

注意

Item必须要挂载组件Layout Element才能成功生成代码

UI文件结构

生成的脚本文件会对应在如下目录:

Scripts
├── UI
│   ├── HotfixView
│   │   └── UIItemBehaviour
│   │       └── Item_TestViewSystem.cs //编写逻辑的脚本
│   │
│   └── ModelView
│       └── UIItemBehaviour
│           └── Item_Test.cs //数据绑定的脚本
└── ...
Item_TestViewSystem.cs
csharp
using UnityEngine;
using UnityEngine.UI;
namespace MH
{
	[EntitySystem]
	public class Scroll_Item_TestAwakeSystem : AwakeSystem<Scroll_Item_Test>
	{
		protected override void Awake(Scroll_Item_Test self)
		{
		}
	}
	[EntitySystem]
	public class Scroll_Item_TestDestroySystem : DestroySystem<Scroll_Item_Test>
	{
		protected override void Destroy(Scroll_Item_Test self)
		{
			self.DestroyWidget();
		}
	}
	public static partial class Scroll_Item_TestViewSystem 
	{
	}
}
Item_Test.cs
csharp
using UnityEngine;
using UnityEngine.UI;
namespace MH
{
	public partial class Scroll_Item_Test : Entity,IAwake,IDestroy,IUIScrollItem<Scroll_Item_Test>,IUILogic 
	{
		public long DataId {get;set;}
		private bool isCacheNode = false;
		public void SetCacheMode(bool isCache)
		{
			this.isCacheNode = isCache;
		}

		public Scroll_Item_Test BindTrans(Transform trans)
		{
			this.uiTransform = trans;
			return this;
		}

		public UnityEngine.UI.Image E_IconImage
     	{
     		get
     		{
     			if (this.uiTransform == null)
     			{
     				Debug.LogError("uiTransform is null.");
     				return null;
     			}
     			if (this.isCacheNode)
     			{
     				if( this.m_E_IconImage == null )
     				{
		    			this.m_E_IconImage = UIFindHelper.FindDeepChild<UnityEngine.UI.Image>(this.uiTransform.gameObject,"E_Icon");
     				}
     				return this.m_E_IconImage;
     			}
     			else
     			{
		    		return UIFindHelper.FindDeepChild<UnityEngine.UI.Image>(this.uiTransform.gameObject,"E_Icon");
     			}
     		}
     	}

		public UnityEngine.UI.Text E_NumText
     	{
     		get
     		{
     			if (this.uiTransform == null)
     			{
     				Debug.LogError("uiTransform is null.");
     				return null;
     			}
     			if (this.isCacheNode)
     			{
     				if( this.m_E_NumText == null )
     				{
		    			this.m_E_NumText = UIFindHelper.FindDeepChild<UnityEngine.UI.Text>(this.uiTransform.gameObject,"E_Icon/E_Num");
     				}
     				return this.m_E_NumText;
     			}
     			else
     			{
		    		return UIFindHelper.FindDeepChild<UnityEngine.UI.Text>(this.uiTransform.gameObject,"E_Icon/E_Num");
     			}
     		}
     	}

		public void DestroyWidget()
		{
			this.m_E_IconImage = null;
			this.m_E_NumText = null;
			this.uiTransform = null;
			this.DataId = 0;
		}

		private UnityEngine.UI.Image m_E_IconImage = null;
		private UnityEngine.UI.Text m_E_NumText = null;
		public Transform uiTransform = null;
	}
}

基础用法

创建循环列表

回到之前的DlgLoginSystem.cs使用刚刚添加的循环列表。

DlgLoginSystem.cs
csharp
using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
using UnityEngine.UI;

namespace MH
{
	public static  class DlgLoginSystem
	{

		public static void RegisterUIEvent(this DlgLogin self)
		{
			self.View.ELoopScrollList_TestLoopHorizontalScrollRect.AddItemRefreshListener(self.OnTestItemLoopRefreshHandler);
		}
		public static void ShowWindow(this DlgLogin self, Entity contextData = null)
		{
			self.Refresh();
		}
		public static void Refresh(this DlgLogin self)
		{
			self.AddUIScrollItems(ref self.ScrollItemTests, 100);
			self.View.ELoopScrollList_TestLoopHorizontalScrollRect.SetVisible(true, 100);
		}
		public static void OnTestItemLoopRefreshHandler(this DlgLogin self, Transform transform, int index)
		{
			var scrollItemServer = self.ScrollItemTests[index].BindTrans(transform);
			scrollItemServer.Init(index);
		}
	}
}
DlgLogin.cs
csharp
using System.Collections.Generic;

namespace MH
{
	public  class DlgLogin :Entity,IAwake,IUILogic
	{

		public DlgLoginViewComponent View { get => this.GetComponent<DlgLoginViewComponent>();}

		public Dictionary<int, Scroll_Item_Test> ScrollItemTests;

	}
}
Scroll_Item_TestViewSystem.cs
csharp

using UnityEngine;
using UnityEngine.UI;
namespace MH
{
	[EntitySystem]
	public class Scroll_Item_TestAwakeSystem : AwakeSystem<Scroll_Item_Test>
	{
		protected override void Awake(Scroll_Item_Test self)
		{
		}
	}
	[EntitySystem]
	public class Scroll_Item_TestDestroySystem : DestroySystem<Scroll_Item_Test>
	{
		protected override void Destroy(Scroll_Item_Test self)
		{
			self.DestroyWidget();
		}
	}
	public static partial class Scroll_Item_TestViewSystem 
	{
		public static void Init(this Scroll_Item_Test self, int index)
		{
			self.E_NumText.text = index.ToString();
		}
	}
}

效果演示

效果视频

以下视频展示了UI框架对循环列表的操作效果演示

这样就完成了对循环列表的操作复制,初始化100个数据并显示复制。

列表项可优化事项

  • 使用对象池
  • ...

技术支持

获取帮助

  • 💬 加入QQ群讨论交流(ET框架群) : 474643097
  • ⭐ 在GitHub上关注项目获取最新更新

Released under the MIT License.