Component List 개발 중

This commit is contained in:
logonkhi
2025-08-08 18:33:29 +09:00
parent 3297a5d1f3
commit 165c3a709f
60 changed files with 7809 additions and 3072 deletions

View File

@@ -36,7 +36,7 @@ RectTransform:
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 59.5}
m_SizeDelta: {x: 200, y: 50}
m_SizeDelta: {x: 300, y: 22}
m_Pivot: {x: 0.5, y: 1}
--- !u!114 &257313229560801817
MonoBehaviour:
@@ -88,8 +88,8 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_AnchoredPosition: {x: 30, y: 0}
m_SizeDelta: {x: -30, y: 0}
m_Pivot: {x: 0, y: 0.5}
--- !u!222 &1829810856472795825
CanvasRenderer:
@@ -112,7 +112,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_Color: {r: 0.8, g: 0.8, b: 0.8, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1

View File

@@ -323,14 +323,14 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_Color: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
m_Sprite: {fileID: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
@@ -3638,10 +3638,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 1709741300}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0.000015258789, y: 0}
m_SizeDelta: {x: 0.000015258713, y: 95.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 1}
--- !u!1 &907694759
GameObject:
@@ -7391,7 +7391,7 @@ RectTransform:
m_AnchorMin: {x: 1, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 20, y: -17}
m_SizeDelta: {x: 10, y: 0}
m_Pivot: {x: 1, y: 1}
--- !u!114 &1884699262
MonoBehaviour:
@@ -7455,14 +7455,14 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_Color: {r: 0.14509805, g: 0.14509805, b: 0.14901961, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0}
m_Sprite: {fileID: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
@@ -7677,7 +7677,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 0.392}
m_Color: {r: 0.14509805, g: 0.14509805, b: 0.14901961, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1

View File

@@ -244,14 +244,14 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_Color: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
m_Sprite: {fileID: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
@@ -2759,7 +2759,7 @@ RectTransform:
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 169, y: 78}
m_SizeDelta: {x: 433.4, y: 401.22}
m_SizeDelta: {x: 300, y: 400}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &726019124
GameObject:
@@ -3572,10 +3572,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 1709741300}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0.000017136335}
m_SizeDelta: {x: 0, y: 95.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 1}
--- !u!1 &907694759
GameObject:
@@ -7013,8 +7013,8 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 2, y: -7.4001946}
m_SizeDelta: {x: 0, y: 14.800389}
m_AnchoredPosition: {x: 2, y: -7.5614367}
m_SizeDelta: {x: 0, y: 15.122873}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &1884020016
MonoBehaviour:
@@ -7228,7 +7228,7 @@ RectTransform:
m_AnchorMin: {x: 1, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 20, y: -17}
m_SizeDelta: {x: 5, y: 0}
m_Pivot: {x: 1, y: 1}
--- !u!114 &1884699262
MonoBehaviour:
@@ -7292,14 +7292,14 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_Color: {r: 0.14509805, g: 0.14509805, b: 0.14901961, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0}
m_Sprite: {fileID: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
@@ -7464,10 +7464,10 @@ RectTransform:
- {fileID: 1884699261}
m_Father: {fileID: 704179583}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 1}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: -171.29999, y: -110}
m_SizeDelta: {x: 342.6, y: 220}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &2007355978
MonoBehaviour:
@@ -7482,14 +7482,14 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 0.392}
m_Color: {r: 0.14509805, g: 0.14509805, b: 0.14901961, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0}
m_Sprite: {fileID: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
@@ -7566,7 +7566,7 @@ MonoBehaviour:
dynamicItemSize: 0
layout:
axis: 1
padding: {x: 0, y: 5}
padding: {x: 0, y: 0}
space: {x: 0, y: 0}
topToBotton: 1
leftToRight: 1

View File

@@ -7,43 +7,103 @@ namespace Gpm.Ui.Sample
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// InfiniteScroll의 다양한 기능을 보여주는 예제 클래스입니다.
/// 데이터 추가, 삭제, 업데이트, 필터링, 특정 위치로 스크롤하는 방법을 구현합니다.
/// </summary>
public class InfiniteScrollSample : MonoBehaviour
{
// Unity 에디터의 인스펙터 창에서 연결할 UI 요소들입니다.
[Header("UI Components")]
[Tooltip("세로 방향으로 스크롤되는 InfiniteScroll 컴포넌트입니다.")]
public InfiniteScroll verticalScrollList = null;
[Tooltip("가로 방향으로 스크롤되는 InfiniteScroll 컴포넌트입니다.")]
public InfiniteScroll horizontalScrollList = null;
[Tooltip("동작 로그를 표시할 UI Text 컴포넌트입니다.")]
public Text logText = null;
[Tooltip("현재 데이터 개수를 표시할 UI Text 컴포넌트입니다.")]
public Text dataCount = null;
[Tooltip("로그 텍스트를 담고 있는 ScrollRect입니다. 로그가 길어질 경우 스크롤을 위함입니다.")]
public ScrollRect logScrollRect = null;
[Tooltip("특정 데이터로 이동할 때 사용할 Dropdown UI입니다.")]
public Dropdown moveDataSelect = null;
[Tooltip("스크롤 이동 애니메이션 타입을 선택할 Dropdown UI입니다.")]
public Dropdown moveDataTypeSelect = null;
[Tooltip("스크롤 이동 시간을 입력할 InputField UI입니다.")]
public InputField moveDataTime = null;
[Tooltip("특정 아이템 인덱스로 이동할 때 사용할 InputField UI입니다.")]
public InputField moveItemSelect = null;
[Tooltip("현재 생성된 아이템(UI 오브젝트)의 개수를 표시할 Text 컴포넌트입니다.")]
public Text itemCount = null;
// 내부 상태 관리를 위한 변수들입니다.
/// <summary>
/// 새로 추가되는 데이터에 고유한 인덱스를 부여하기 위한 카운터입니다.
/// </summary>
private int index = 0;
/// <summary>
/// 총 몇 개의 데이터가 삽입되었는지 추적하는 카운터입니다.
/// </summary>
private int insertCount = 0;
/// <summary>
/// 총 몇 개의 데이터가 제거되었는지 추적하는 카운터입니다.
/// </summary>
private int removeCount = 0;
/// <summary>
/// 스크롤 뷰에 표시될 모든 데이터를 관리하는 리스트입니다.
/// InfiniteScroll은 이 리스트의 데이터를 기반으로 UI 아이템을 생성하고 재활용합니다.
/// </summary>
private List<TestItemData> dataList = new List<TestItemData>();
/// <summary>
/// 화면에 로그를 표시하기 위한 StringBuilder입니다.
/// 문자열을 반복적으로 합칠 때 성능상 이점을 가집니다.
/// </summary>
private StringBuilder log = new StringBuilder();
/// <summary>
/// 스크립트가 처음 활성화될 때 호출되는 Unity 생명주기 메서드입니다.
/// 주로 이벤트 리스너를 등록하고 초기화 작업을 수행합니다.
/// </summary>
private void Start()
{
// 세로 스크롤 리스트에서 아이템이 선택되었을 때 호출될 콜백 함수를 등록합니다.
// 사용자가 아이템을 클릭하면 해당 아이템의 데이터 정보가 로그에 표시됩니다.
verticalScrollList.AddSelectCallback((data) =>
{
// 콜백으로 받은 데이터(data)는 InfiniteScrollData 타입이므로,
// 실제 데이터 타입인 TestItemData로 형변환하여 사용합니다.
AddLog(string.Format("vertical select data : {0}", ((TestItemData)data).index.ToString()));
});
// 가로 스크롤 리스트에 대한 선택 콜백도 동일하게 등록합니다.
horizontalScrollList.AddSelectCallback((data) =>
{
AddLog(string.Format("horizontal select data : {0}", ((TestItemData)data).index.ToString()));
});
// 'MoveToData' 드롭다운의 값이 변경될 때 호출될 리스너를 등록합니다.
// 선택된 옵션의 인덱스를 기반으로 해당 데이터 위치로 스크롤을 이동시킵니다.
moveDataSelect.onValueChanged.AddListener((option) =>
{
MoveToDataIndex(option);
});
// 'MoveToItem' 입력 필드의 값이 변경될 때 호출될 리스너를 등록합니다.
// 입력된 아이템 인덱스로 스크롤을 이동시킵니다.
moveItemSelect.onValueChanged.AddListener((text) =>
{
int index = 0;
@@ -57,45 +117,72 @@ namespace Gpm.Ui.Sample
}
/// <summary>
/// 스크롤 뷰가 마지막 데이터 위치에 도달했는지 확인하고 결과를 로그에 출력합니다.
/// InfiniteScroll.IsMoveToLastData() API의 사용 예시입니다.
/// </summary>
public void IsMoveToLastData()
{
AddLog(string.Format("Is move to last data vertical:{0},horizontal:{1}",
verticalScrollList.IsMoveToLastData().ToString(), horizontalScrollList.IsMoveToLastData().ToString()));
}
/// <summary>
/// 새로운 데이터를 생성하여 스크롤 리스트의 끝에 추가합니다.
/// InfiniteScroll.InsertData() API의 사용 예시입니다.
/// </summary>
public void InsertData()
{
// 1. 새로운 데이터 객체를 생성하고 고유 인덱스를 할당합니다.
TestItemData data = new TestItemData();
data.index = index++;
// 2. 내부 데이터 리스트(dataList)에 추가하여 데이터를 관리합니다.
dataList.Add(data);
// 3. InfiniteScroll 컴포넌트에 데이터를 삽입하여 UI에 반영합니다.
// 이 호출로 인해 스크롤 뷰는 새로운 아이템을 표시할 준비를 합니다.
verticalScrollList.InsertData(data);
horizontalScrollList.InsertData(data);
// 4. 'MoveToData' 드롭다운에도 해당 데이터 인덱스를 옵션으로 추가합니다.
var options = new List<Dropdown.OptionData>() { new Dropdown.OptionData(data.index.ToString()) };
moveDataSelect.AddOptions(options);
// 5. 카운터를 업데이트하고 로그를 남깁니다.
++insertCount;
UpdateDataCount();
AddLog(string.Format("Insert Data : {0}", index - 1));
}
/// <summary>
/// 스크롤 리스트의 모든 데이터를 제거합니다.
/// InfiniteScroll.Clear() API의 사용 예시입니다.
/// </summary>
public void Clear()
{
// 1. InfiniteScroll의 모든 데이터를 제거합니다.
// 이 메서드는 내부 데이터 목록과 화면의 UI 아이템을 모두 정리합니다.
verticalScrollList.Clear();
horizontalScrollList.Clear();
// 2. 이 예제에서 사용하는 내부 데이터 리스트와 UI 요소도 함께 정리합니다.
removeCount += dataList.Count;
dataList.Clear();
moveDataSelect.ClearOptions();
// 3. 카운터를 업데이트하고 로그를 남깁니다.
UpdateDataCount();
AddLog("Clear Data");
}
/// <summary>
/// 데이터 리스트에서 무작위로 하나의 데이터를 선택하여 제거합니다.
/// InfiniteScroll.RemoveData() API의 사용 예시입니다.
/// </summary>
public void Remove()
{
if (dataList.Count <= 0)
@@ -103,25 +190,35 @@ namespace Gpm.Ui.Sample
return;
}
// 1. 무작위로 데이터 인덱스를 선택합니다.
int dataIndex = GetDataIndexByRandom();
TestItemData data = dataList[dataIndex];
// 2. InfiniteScroll에서 해당 데이터를 제거합니다.
// 스크롤 뷰는 이 데이터를 표시하던 UI 아이템을 숨기거나 재활용합니다.
verticalScrollList.RemoveData(data);
horizontalScrollList.RemoveData(data);
// 3. 내부 데이터 리스트와 UI 드롭다운에서도 해당 데이터를 제거합니다.
dataList.Remove(data);
int optionIndex = moveDataSelect.options.FindIndex(p => p.text.Equals(data.index.ToString()));
if (optionIndex != -1)
{
moveDataSelect.options.RemoveAt(optionIndex);
moveDataSelect.options.RemoveAt(optionIndex); // 드롭다운 표시값 갱신
}
// 4. 카운터를 업데이트하고 로그를 남깁니다.
removeCount++;
UpdateDataCount();
AddLog(string.Format("Remove data : {0}", data.index));
}
/// <summary>
/// 무작위로 하나의 데이터를 선택하여 내용을 업데이트합니다.
/// InfiniteScroll.UpdateData() API의 사용 예시입니다.
/// </summary>
public void UpdateData()
{
if (dataList.Count <= 0)
@@ -129,36 +226,56 @@ namespace Gpm.Ui.Sample
return;
}
// 1. 무작위로 업데이트할 데이터를 선택합니다.
int dataIndex = GetDataIndexByRandom();
TestItemData data = dataList[dataIndex];
// 2. 데이터의 내용을 변경합니다. (예: 설명 텍스트 업데이트)
data.description = string.Format("Updated : {0}", DateTime.Now.ToString("T"));
// 3. InfiniteScroll에 데이터가 변경되었음을 알립니다.
// 이 호출을 받은 스크롤 뷰는 해당 데이터를 표시하는 UI 아이템을 찾아 내용을 새로고침합니다.
verticalScrollList.UpdateData(data);
horizontalScrollList.UpdateData(data);
AddLog(string.Format("Update data : {0}", data.index));
}
/// <summary>
/// 스크롤 리스트의 모든 데이터를 업데이트합니다.
/// InfiniteScroll.UpdateAllData() API의 사용 예시입니다.
/// </summary>
public void UpdateAllData()
{
// 1. 내부 데이터 리스트의 모든 항목을 순회하며 내용을 변경합니다.
for (int index = 0; index < dataList.Count; ++index)
{
dataList[index].description = string.Format("Updated : {0}", DateTime.Now.ToString("T"));
}
// 2. InfiniteScroll에 모든 데이터가 변경되었음을 알립니다.
// 스크롤 뷰는 현재 화면에 보이는 모든 아이템을 새로고침합니다.
verticalScrollList.UpdateAllData();
horizontalScrollList.UpdateAllData();
AddLog("Update all data");
}
/// <summary>
/// 데이터 및 아이템 개수를 UI에 최신 정보로 업데이트합니다.
/// </summary>
private void UpdateDataCount()
{
// dataList.Count는 원본 데이터의 총 개수입니다.
dataCount.text = string.Format("Data Count : {0} (insert[{1}] remove[{2}])", dataList.Count, insertCount, removeCount);
// InfiniteScroll.GetItemCount()는 필터링이 적용된 후, 실제 화면에 표시될 아이템의 개수입니다.
itemCount.text = verticalScrollList.GetItemCount().ToString();
}
/// <summary>
/// 스크롤을 가장 처음 위치로 이동시킵니다.
/// </summary>
public void MoveToFirstData()
{
verticalScrollList.MoveToFirstData();
@@ -167,6 +284,9 @@ namespace Gpm.Ui.Sample
AddLog("Move to first data");
}
/// <summary>
/// 스크롤을 가장 마지막 위치로 이동시킵니다.
/// </summary>
public void MoveToLastData()
{
verticalScrollList.MoveToLastData();
@@ -175,6 +295,11 @@ namespace Gpm.Ui.Sample
AddLog("Move to last data");
}
/// <summary>
/// 지정된 '아이템 인덱스'로 스크롤을 이동시킵니다.
/// '아이템 인덱스'는 필터링과 정렬이 적용된 후 화면에 보이는 순서입니다.
/// </summary>
/// <param name="itemIndex">이동할 대상의 아이템 인덱스</param>
private void MoveToItemIndex(int itemIndex)
{
float time = 0;
@@ -183,11 +308,18 @@ namespace Gpm.Ui.Sample
AddLog("Time is not Number");
}
// MoveTo API를 사용하여 (아이템 인덱스, 이동 타입, 이동 시간)을 인자로 전달합니다.
verticalScrollList.MoveTo(itemIndex, (InfiniteScroll.MoveToType)moveDataTypeSelect.value, time);
horizontalScrollList.MoveTo(itemIndex, (InfiniteScroll.MoveToType)moveDataTypeSelect.value, time);
AddLog(string.Format("Move to ItemIndex : {0}", itemIndex));
}
/// <summary>
/// 지정된 '데이터 인덱스'로 스크롤을 이동시킵니다.
/// '데이터 인덱스'는 원본 데이터 리스트(dataList)에서의 순서입니다.
/// </summary>
/// <param name="dataIndex">이동할 대상의 데이터 인덱스</param>
private void MoveToDataIndex(int dataIndex)
{
float time = 0;
@@ -195,14 +327,18 @@ namespace Gpm.Ui.Sample
{
AddLog("Time is not Number");
}
// MoveToFromDataIndex 또는 MoveTo(data) API를 사용할 수 있습니다.
// 여기서는 두 가지 방법을 무작위로 선택하여 보여줍니다.
if (UnityEngine.Random.Range(0, 2) == 0)
{
// 방법 1: 데이터 인덱스를 직접 사용
verticalScrollList.MoveToFromDataIndex(dataIndex, (InfiniteScroll.MoveToType)moveDataTypeSelect.value, time);
horizontalScrollList.MoveToFromDataIndex(dataIndex, (InfiniteScroll.MoveToType)moveDataTypeSelect.value, time);
}
else
{
// 방법 2: 데이터 객체를 직접 사용
TestItemData data = dataList[dataIndex];
verticalScrollList.MoveTo(data, (InfiniteScroll.MoveToType)moveDataTypeSelect.value, time);
@@ -212,30 +348,42 @@ namespace Gpm.Ui.Sample
AddLog(string.Format("Move to DataIndex : {0}", dataIndex));
}
/// <summary>
/// 홀수 인덱스를 가진 데이터만 표시하도록 필터를 설정합니다.
/// InfiniteScroll.SetFilter() API의 사용 예시입니다.
/// </summary>
public void FilterOdd()
{
// Predicate<InfiniteScrollData>는 데이터를 받아 bool을 반환하는 함수입니다.
// true를 반환하는 데이터만 화면에 표시됩니다.
Predicate<InfiniteScrollData> func = (data) =>
{
if (data is TestItemData testData)
{
// 인덱스가 짝수인 데이터만 남깁니다 (홀수번째 아이템).
return (testData.index % 2) == 0;
}
return false;
};
// 생성한 필터 함수를 스크롤 뷰에 설정합니다.
verticalScrollList.SetFilter(func);
horizontalScrollList.SetFilter(func);
UpdateDataCount();
}
/// <summary>
/// 짝수 인덱스를 가진 데이터만 표시하도록 필터를 설정합니다.
/// </summary>
public void FilterEven()
{
Predicate<InfiniteScrollData> func = (data) =>
{
if (data is TestItemData testData)
{
// 인덱스가 홀수인 데이터만 남깁니다 (짝수번째 아이템).
return (testData.index % 2) == 1;
}
@@ -248,21 +396,36 @@ namespace Gpm.Ui.Sample
UpdateDataCount();
}
/// <summary>
/// 모든 필터를 제거하고 모든 데이터를 표시합니다.
/// </summary>
public void FilterAll()
{
// SetFilter에 null을 전달하면 필터가 해제됩니다.
verticalScrollList.SetFilter(null);
horizontalScrollList.SetFilter(null);
UpdateDataCount();
}
/// <summary>
/// 화면의 로그 UI에 새로운 텍스트를 추가하고, 로그 창을 맨 아래로 스크롤합니다.
/// </summary>
/// <param name="text">로그에 추가할 문자열</param>
private void AddLog(string text)
{
log.AppendLine(text);
logText.text = log.ToString();
// 로그가 추가될 때마다 스크롤을 맨 아래로 내려 최신 로그를 보여줍니다.
// verticalNormalizedPosition = 0.0f가 맨 아래를 의미합니다.
logScrollRect.verticalNormalizedPosition = 0.0f;
}
/// <summary>
/// 현재 데이터 리스트에서 유효한 랜덤 인덱스를 반환합니다.
/// </summary>
/// <returns>0과 (dataList.Count - 1) 사이의 랜덤 정수</returns>
private int GetDataIndexByRandom()
{
return UnityEngine.Random.Range(0, dataList.Count);

View File

@@ -1,29 +1,64 @@
namespace Gpm.Ui.Sample
namespace Gpm.Ui.Sample
{
using Gpm.Ui;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// InfiniteScroll의 각 아이템에 표시될 데이터를 정의하는 클래스입니다.
/// InfiniteScroll에 데이터를 사용하려면 반드시 `InfiniteScrollData` 클래스를 상속받아야 합니다.
/// 이 클래스는 데이터 모델(Model)의 역할을 합니다.
/// </summary>
public class TestItemData : InfiniteScrollData
{
/// <summary>
/// 각 데이터를 고유하게 식별하기 위한 인덱스입니다.
/// 예제에서는 이 값을 사용하여 아이템에 번호를 표시합니다.
/// </summary>
public int index = 0;
/// <summary>
/// 아이템에 표시될 추가적인 설명 텍스트입니다.
/// 예제에서는 데이터가 업데이트될 때 이 값을 변경하여 보여줍니다.
/// </summary>
public string description = string.Empty;
}
/// <summary>
/// InfiniteScroll에 실제로 표시되는 UI 아이템(Prefab)을 제어하는 클래스입니다.
/// 이 클래스는 `InfiniteScrollItem`을 상속받아야 하며, 아이템의 UI 업데이트와 사용자 상호작용을 처리합니다.
/// 이 스크립트는 아이템으로 사용될 프리팹에 컴포넌트로 추가되어야 합니다.
/// 뷰(View)와 컨트롤러(Controller)의 역할을 합니다.
/// </summary>
public class TestItem : InfiniteScrollItem
{
[Tooltip("아이템의 텍스트를 표시할 UI Text 컴포넌트입니다. Unity 에디터의 인스펙터 창에서 연결해야 합니다.")]
public Text text = null;
[Tooltip("이 아이템이 세로 스크롤용인지 여부를 나타냅니다. 현재 예제에서는 사용되지 않지만, 필요에 따라 레이아웃을 구분하는 데 사용할 수 있습니다.")]
public bool isVertical = true;
/// <summary>
/// InfiniteScroll에 의해 데이터가 이 아이템에 할당될 때 호출되는 메서드입니다.
/// 이 메서드를 재정의(override)하여 `scrollData`를 실제 데이터 타입(`TestItemData`)으로 변환하고,
/// 그 데이터를 사용하여 UI 요소(텍스트, 이미지 등)를 업데이트해야 합니다.
/// 이 메서드는 아이템이 재활용될 때마다 새로운 데이터로 호출됩니다.
/// </summary>
/// <param name="scrollData">이 아이템에 표시할 데이터입니다. `InfiniteScrollData`를 상속받은 객체입니다.</param>
public override void UpdateData(InfiniteScrollData scrollData)
{
// 1. 부모 클래스의 UpdateData를 호출하여 기본 초기화 작업을 수행합니다.
base.UpdateData(scrollData);
// 2. 매개변수로 받은 scrollData를 실제 사용할 데이터 타입인 TestItemData로 형변환(casting)합니다.
TestItemData itemData = (TestItemData)scrollData;
// 3. StringBuilder를 사용하여 표시할 문자열을 효율적으로 구성합니다.
StringBuilder sb = new StringBuilder();
sb.Append(string.Format("Item : {0} ", itemData.index));
// 4. 데이터의 인덱스가 짝수인지 홀수인지에 따라 추가 텍스트를 붙입니다.
if (itemData.index % 2 == 0)
{
sb.Append("(Even) ");
@@ -33,19 +68,43 @@
sb.Append("(Odd) ");
}
// 5. 데이터에 포함된 설명 텍스트를 추가합니다.
sb.Append(itemData.description);
// 6. 최종적으로 만들어진 문자열을 UI Text 컴포넌트에 할당하여 화면에 표시합니다.
text.text = sb.ToString();
}
/// <summary>
/// 이 아이템이 클릭되었을 때 호출되는 메서드입니다.
/// 이 메서드는 Unity 에디터에서 아이템 프리팹에 있는 Button 컴포넌트의 OnClick() 이벤트에 연결해야 합니다.
/// </summary>
/// <example>
/// 1. 아이템 프리팹에 Button 컴포넌트를 추가합니다.
/// 2. Button 컴포넌트의 OnClick() 이벤트 리스너에 이 아이템 자기 자신을 등록합니다.
/// 3. 드롭다운 메뉴에서 `TestItem` -> `OnClick()`을 선택합니다.
/// </example>
public void OnClick()
{
// 부모 클래스(InfiniteScrollItem)의 OnSelect() 메서드를 호출합니다.
// 이 메서드는 InfiniteScroll에 '이 아이템이 선택되었다'는 사실을 알리고,
// InfiniteScrollSample에서 등록한 `AddSelectCallback` 콜백 함수를 실행시킵니다.
OnSelect();
}
/// <summary>
/// 아이템의 크기를 동적으로 변경할 때 호출되는 예제 메서드입니다.
/// 이 메서드 또한 Button의 OnClick() 이벤트에 연결하여 사용할 수 있습니다.
/// 이 기능은 InfiniteScroll 컴포넌트의 `dynamicItemSize` 옵션이 활성화되어 있을 때만 정상적으로 동작합니다.
/// </summary>
public void OnChangeSizeClick()
{
// 30에서 400 사이의 랜덤한 크기 값을 생성합니다.
float size = Random.Range(30, 400);
// 부모 클래스의 SetSize() 메서드를 호출하여 아이템의 크기를 변경합니다.
// 스크롤 방향이 Vertical이면 높이(height)가, Horizontal이면 너비(width)가 변경됩니다.
// InfiniteScroll은 이 변경사항을 감지하고 레이아웃을 다시 계산합니다.
SetSize(size);
}
}