Skip to content

Loop List

Introduction

Description

In game development, we often need to display large amounts of list data, such as:

  • Hundreds or thousands of items in inventory systems
  • Large amounts of player data in leaderboards
  • Message history in chat systems
  • Product lists in shops

If we create a UI object for each data item, it would not only consume a lot of memory but also cause serious performance issues. The loop list reuses a small number of UI objects to display large amounts of data, only creating objects within the visible area and dynamically updating content when scrolling. This mechanism greatly reduces memory usage and improves performance, making it a best practice for handling large amounts of list data in game development.

Core Features

  • Object Reuse: Only creates items needed in the visible area
  • Automatic Recycling: Items outside the visible area are automatically recycled
  • Performance Optimization: Minimizes memory usage and rendering overhead
  • Flexible Configuration: Supports vertical and horizontal scrolling, grid layouts

UI Interface Code Usage Demonstration

Video Tutorial

The following video demonstrates the process of generating common UI component code using the UI framework

Video Operation Introduction

  1. Creating Loop List: As seen in the video, a Horizontal type loop list was created, named: ELoopScrollList_Test
  2. Creating Loop List Item: Also created an object named: Item_Test, placed in the Assets/Bundles/UI/Item folder, and generated corresponding code files like with Common
  3. Specifying Item for Loop List: Also specified the name of the Item prefab to be used on the Loop Horizontal Scroll Rect component on the loop list ELoopScrollList_Test, which will later be loaded through YooAsset resource management

Note

Item must have the Layout Element component attached to successfully generate code

UI File Structure

Generated script files will be in the following directories:

Scripts
├── UI
│   ├── HotfixView
│   │   └── UIItemBehaviour
│   │       └── Item_TestViewSystem.cs //Logic script
│   │
│   └── ModelView
│       └── UIItemBehaviour
│           └── Item_Test.cs //Data binding script
└── ...
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;
	}
}

Basic Usage

Creating Loop List

Back to the previously mentioned DlgLoginSystem.cs using the loop list we just added.

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();
		}
	}
}

Effect Demonstration

Effect Video

The following video demonstrates the operation effect of the loop list in the UI framework

This completes the operation of the loop list, initializing and displaying 100 data items.

List Item Optimization Considerations

  • Use object pooling
  • ...

Technical Support

Get Help

  • 💬 Join QQ group for discussion (ET Framework Group) : 474643097
  • ⭐ Follow the project on GitHub for the latest updates

Released under the MIT License.