/* The MIT License (MIT)
Copyright (c) 2014 Orange Labs UK
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class InfiniteListPopulator : MonoBehaviour {
// events to listen to if needed...
public delegate void InfiniteItemIsPressed(int itemDataIndex, bool isDown);
public event InfiniteItemIsPressed InfiniteItemIsPressedEvent;
public delegate void InfiniteItemIsClicked(int itemDataIndex);
public event InfiniteItemIsClicked InfiniteItemIsClickedEvent;
public bool enableLog = true;
//Prefabs
const string listItemTag = "listItem";
const string listSectionTag = "listSection";
public Transform itemPrefab;
public Transform sectionPrefab;
// NGUI Controllers
public UITable table;
public UIScrollView draggablePanel;
//scroll indicator
public Transform scrollIndicator;
private int scrollCursor = 0;
// pool
public float cellHeight = 94f;// at the moment we support fixed height... insert here or measure it 아이템의 높이 값을 저장한다 이를 기준으로 스크롤바 위치와 만들어질 아이템 개수를 선정한다.
private int poolSize = 6;
private List itemsPool = new List();
private int extraBuffer = 10;
private int startIndex = 0; // where to start 초기 값을 기준으로 아이템을 정렬 jump to item에서 사용 할수있다.
private Hashtable dataTracker = new Hashtable();// hashtable to keep track of what is being displayed by the pool 풀 인덱스 값과 데이터 인덱스 값을 가진다.
//our data...using arraylist for generic types... if you want specific types just refactor it to List where T is the type
// 리스트 뷰에 기본데이터 저장
private ArrayList originalData = new ArrayList();
private ArrayList dataList = new ArrayList();
//our sections
// 섹션 정보 저장
private int numberOfSections = 0;
private List sectionsIndices = new List();
#region Examples
public void NoSections()
{
InitTableView(originalData,null,0);
}
public void JumpTo30()
{
SetStartIndex(30);
RefreshTableView();
}
public void ThreeSections()
{
List indices = new List();
indices.Add(0);
indices.Add(5);
indices.Add(10);
InitTableView(originalData,indices,0);
}
public void StartDemo()
{
// mock data for the demo 기본 정수데이터를 리스트에 표시한다.
ArrayList dataList = new ArrayList();
for(int i= 0; i<50 ;i++)
{
string number = "row"+i;
dataList.Add(number);
}
// with some sections 섹션 위치를 저장한다.
List indices = new List();
indices.Add(0);
indices.Add(10);
indices.Add(30);
InitTableView(dataList,indices,0);
}
#endregion
#region Start & Update for MonoBehaviour
void Start () {
// check prefabs
if(itemPrefab == null)
Debug.LogError("InfiniteListPopulator:: itemPrefab is not assigned");
else if(!itemPrefab.tag.Equals(listItemTag))
Debug.LogError("InfiniteListPopulator:: itemPrefab tag should be "+listItemTag);
if(sectionPrefab == null)
Debug.LogError("InfiniteListPopulator:: sectionPrefab is not assigned");
else if(!sectionPrefab.tag.Equals(listSectionTag))
Debug.LogError("InfiniteListPopulator:: sectionPrefab tag should be "+listSectionTag);
// for the demo
StartDemo();
}
// Update is called once per frame
void Update () {
// scroll indicator stuff... TODO: Add switch to turn it ON/OFF
if(Mathf.Abs(draggablePanel.currentMomentum.y)>0 && scrollIndicator.GetComponent().from > 0)
{
scrollIndicator.GetComponent().from = 0;
scrollIndicator.GetComponent().to = 0.25f;
scrollIndicator.GetComponent().duration = 0.5f;
scrollIndicator.GetComponent().enabled = true;
scrollIndicator.GetComponent().delay = 0f;
TweenAlpha.Begin(scrollIndicator.gameObject,0f);
}
if(Mathf.Abs(draggablePanel.currentMomentum.y)== 0 && scrollIndicator.GetComponent().to >0)
{
scrollIndicator.GetComponent().from = 0.25f;
scrollIndicator.GetComponent().to = 0;
scrollIndicator.GetComponent().duration = 0.5f;
scrollIndicator.GetComponent().enabled = true;
scrollIndicator.GetComponent().delay = 1f;
TweenAlpha.Begin(scrollIndicator.gameObject,0.25f);
}
float posY = 0;
posY = -scrollCursor/(float)dataList.Count * draggablePanel.panel.baseClipRegion.w;
scrollIndicator.localPosition = new Vector3(scrollIndicator.localPosition.x,posY,scrollIndicator.localPosition.z);
}
#endregion
#region Infinite List Data Sources calls
/*
* These methods are used mainly to populate
* the data for both sections and rows..
* change to suit your implementation
*
* */
string GetTitleForSection(int i)
{
return "Section "+i;
}
void PopulateListSectionWithIndex(Transform item, int index)
{
item.GetComponent().label.text = GetTitleForSection(index);
}
void PopulateListItemWithIndex(Transform item, int dataIndex)
{
item.GetComponent().label.text = dataList[dataIndex] as string;// casting to our class...
}
#endregion
#region Infinite List Management & scrolling
// set then call InitTableView
public void SetStartIndex(int inStartIndex)
{
startIndex = GetJumpIndexForItem(inStartIndex);
}
public void SetOriginalData(ArrayList inDataList)
{
originalData = new ArrayList(inDataList);
}
public void SetSectionIndices(List inSectionsIndices)
{
numberOfSections = inSectionsIndices.Count;
sectionsIndices = inSectionsIndices;
}
// call to refresh without changing sections.. e.g. jump to specific point...
public void RefreshTableView()
{
if(enableLog)
{
if(originalData == null || originalData.Count == 0)
Debug.LogWarning("InfiniteListPopulator.InitTableView() trying to refresh with no data");
}
InitTableView(originalData,sectionsIndices,startIndex);
}
public void InitTableView(ArrayList inDataList, List inSectionsIndices, int inStartIndex)
{
InitTableViewImp(inDataList,inSectionsIndices,inStartIndex);
}
#endregion
#region The private stuff... ideally you shouldn't need to call or change things directly from this region onwards
void InitTableViewImp(ArrayList inDataList, List inSectionsIndices, int inStartIndex)
{
RefreshPool();
startIndex = inStartIndex;
scrollCursor = inStartIndex;
dataTracker.Clear();
originalData = new ArrayList(inDataList);
dataList = new ArrayList(inDataList);
if(inSectionsIndices!=null)
{
numberOfSections = inSectionsIndices.Count;
sectionsIndices = inSectionsIndices;
}
else
{
numberOfSections = 0;
sectionsIndices = new List();
}
// do we have a section? then inject 'special' data inside the date list
if(numberOfSections > 0)
{
for(int i = 0; i< numberOfSections; i++)
{
sectionsIndices[i] +=i;
if(sectionsIndices[i]().itemNumber = poolIndex;
item.name = "item"+dataIndex;
item.parent = table.transform;
item.GetComponent().itemDataIndex = dataIndex;
item.GetComponent().listPopulator = this;
item.GetComponent().panel = draggablePanel.panel;
PopulateListSectionWithIndex(item,sectionsIndices.IndexOf(dataIndex));
itemsPool[poolIndex] = item;
dataTracker.Add(itemsPool[poolIndex].GetComponent().itemDataIndex,itemsPool[poolIndex].GetComponent().itemNumber);
}
void ChangeItemToSection(Transform item, int newIndex,int oldIndex)
{
int j = 0;
Vector3 lastPosition = Vector3.zero;
if(item.tag.Equals(listItemTag))
j = item.GetComponent().itemNumber;
if(item.tag.Equals(listSectionTag))
j = item.GetComponent().itemNumber;
lastPosition = item.localPosition;
Object.Destroy(item.gameObject);
item = Instantiate(sectionPrefab) as Transform;
item.parent = table.transform;
item.localPosition = lastPosition;
if(newIndex ().itemNumber = j;
item.GetComponent().itemDataIndex = newIndex;
item.GetComponent().listPopulator = this;
item.GetComponent().panel = draggablePanel.panel;
PopulateListSectionWithIndex(item,sectionsIndices.IndexOf(newIndex));
itemsPool[j] = item;
dataTracker.Add(newIndex,(int)(dataTracker[oldIndex]));
dataTracker.Remove(oldIndex);
}
// items
void InitListItemWithIndex(Transform item, int dataIndex, int poolIndex)
{
item.GetComponent().itemDataIndex = dataIndex;
item.GetComponent().listPopulator = this;
item.GetComponent().panel = draggablePanel.panel;
item.name = "item"+dataIndex;
PopulateListItemWithIndex(item,dataIndex);
dataTracker.Add(itemsPool[poolIndex].GetComponent().itemDataIndex,itemsPool[poolIndex].GetComponent().itemNumber);
}
void PrepareListItemWithIndex(Transform item, int newIndex,int oldIndex)
{
if(newIndex ().itemDataIndex=newIndex;
item.name = "item"+(newIndex);
PopulateListItemWithIndex(item,newIndex);
dataTracker.Add(newIndex,(int)(dataTracker[oldIndex]));
dataTracker.Remove(oldIndex);
}
void ChangeSectionToItem(Transform item, int newIndex,int oldIndex)
{
int j = 0;
Vector3 lastPosition = Vector3.zero;
j = item.GetComponent().itemNumber;
lastPosition = item.localPosition;
Object.Destroy(item.gameObject);
item = Instantiate(itemPrefab) as Transform;
item.parent = table.transform;
item.localPosition = lastPosition;
item.GetComponent().itemNumber = j;
item.GetComponent().listPopulator = this;
item.GetComponent().panel = draggablePanel.panel;
itemsPool[j] = item;
PrepareListItemWithIndex(item,newIndex,oldIndex);
}
// the main logic for "infinite scrolling"...
private bool isUpdatingList = false;
///
/// 아이템이 자신의 라벨이 패널에서 자신의 라벨이 보이지않을때 호출한다.
/// DataTracker와 ItemList ItemNumber , DataIndex를 통해 업 스크롤 다운스크롤을 확인
/// 이이후 해당 아이템의 정보와 위치를 바꾼다.
///
///
///
public IEnumerator ItemIsInvisible(int itemNumber)
{
if(isUpdatingList) yield return null;
isUpdatingList = true;
if(dataList.Count > poolSize)// we need to do something "smart"...
{
Transform item = itemsPool[itemNumber];
int itemDataIndex = 0;
if(item.tag.Equals(listItemTag))
itemDataIndex = item.GetComponent().itemDataIndex;
if(item.tag.Equals(listSectionTag))
itemDataIndex = item.GetComponent().itemDataIndex;
int indexToCheck=0;
InfiniteItemBehavior infItem = null;
InfiniteSectionBehavior infSection = null;
if(dataTracker.ContainsKey(itemDataIndex+1))
{
infItem = itemsPool[(int)(dataTracker[itemDataIndex+1])].GetComponent();
infSection = itemsPool[(int)(dataTracker[itemDataIndex+1])].GetComponent();
if((infItem != null && infItem.verifyVisibility()) || (infSection != null && infSection.verifyVisibility()))
{
// dragging upwards (scrolling down)
indexToCheck = itemDataIndex -(extraBuffer/2);
if(dataTracker.ContainsKey(indexToCheck))
{
//do we have an extra item(s) as well?
for(int i = indexToCheck; i>=0; i--)
{
if(dataTracker.ContainsKey(i))
{
infItem = itemsPool[(int)(dataTracker[i])].GetComponent();
infSection = itemsPool[(int)(dataTracker[i])].GetComponent();
if((infItem != null && !infItem.verifyVisibility()) || (infSection != null && !infSection.verifyVisibility()))
{
item = itemsPool[(int)(dataTracker[i])];
if((i)+poolSize < dataList.Count && i>-1)
{
// is it a section index?
if(sectionsIndices.Contains(i+poolSize))
{
// change item to section
ChangeItemToSection(item,i+poolSize,i);
}
else if(item.tag.Equals(listSectionTag))
{
// change section to item
ChangeSectionToItem(item,i+poolSize,i);
}
else
{
PrepareListItemWithIndex(item,i+poolSize,i);
}
}
}
}
else
{
scrollCursor = itemDataIndex-1;
break;
}
}
}
}
}
if(dataTracker.ContainsKey(itemDataIndex-1))
{
infItem = itemsPool[(int)(dataTracker[itemDataIndex-1])].GetComponent();
infSection = itemsPool[(int)(dataTracker[itemDataIndex-1])].GetComponent();
if((infItem != null && infItem.verifyVisibility()) || (infSection != null && infSection.verifyVisibility()))
{
//dragging downwards check the item below
indexToCheck = itemDataIndex +(extraBuffer/2);
if(dataTracker.ContainsKey(indexToCheck))
{
// if we have an extra item
for(int i = indexToCheck; i();
infSection = itemsPool[(int)(dataTracker[i])].GetComponent();
if((infItem != null && !infItem.verifyVisibility()) || (infSection != null && !infSection.verifyVisibility()))
{
item = itemsPool[(int)(dataTracker[i])];
if((i)-poolSize > -1 && (i) < dataList.Count)
{
// is it a section index?
if(sectionsIndices.Contains(i-poolSize))
{
// change item to section
ChangeItemToSection(item,i-poolSize,i);
}
else if(item.tag.Equals(listSectionTag))
{
// change section to item
ChangeSectionToItem(item,i-poolSize,i);
}
else
{
PrepareListItemWithIndex(item,i-poolSize,i);
}
}
}
}
else
{
scrollCursor = itemDataIndex+1;
break;
}
}
}
}
}
}
isUpdatingList = false;
}
#endregion
#region items callbacks and helpers
int GetJumpIndexForItem(int itemDataIndex)
{
// find real data index
if(numberOfSections>0 && itemDataIndex > sectionsIndices[0])
{
for(int index = numberOfSections-1; index >=0; index--)
{
if(itemDataIndex > sectionsIndices[index])
{
itemDataIndex = itemDataIndex + (index+1);
break;
}
}
}
return itemDataIndex;
}
public int GetRealIndexForItem(int itemDataIndex)
{
// find real data index
if(numberOfSections>0 && itemDataIndex > sectionsIndices[0])
{
for(int index = numberOfSections-1; index >=0; index--)
{
if(itemDataIndex > sectionsIndices[index])
{
itemDataIndex = itemDataIndex - (index+1);
break;
}
}
}
return itemDataIndex;
}
public void itemIsPressed(int itemDataIndex,bool isDown)
{
itemDataIndex = GetRealIndexForItem(itemDataIndex);
if(enableLog)
{
Debug.Log("Pressed down item "+ itemDataIndex +" "+isDown);
}
if(InfiniteItemIsPressedEvent!=null)
InfiniteItemIsPressedEvent(itemDataIndex,isDown);
}
public void itemClicked(int itemDataIndex)
{
itemDataIndex = GetRealIndexForItem(itemDataIndex);
if(enableLog)
{
Debug.Log("Clicked item "+ itemDataIndex);
}
if(InfiniteItemIsClickedEvent!=null)
InfiniteItemIsClickedEvent(itemDataIndex);
}
#endregion
#region Pool & sections Management
Transform GetItemFromPool(int i)
{
if(i >= 0 && i< poolSize)
{
itemsPool[i].gameObject.SetActive(true);
return itemsPool[i];
}
else
return null;
}
void RefreshPool()
{
poolSize = (int)(draggablePanel.panel.baseClipRegion.w/cellHeight) + extraBuffer; // 소프트클립 패널 높이와 아이템 높이을 나눈 값에 10개의 버퍼를 더두어 아이템을 생성
if(enableLog)
{
Debug.Log("REFRESH POOL SIZE:::"+poolSize);
}
// destroy current items
for(int i=0; i< itemsPool.Count; i++)
{
Object.Destroy(itemsPool[i].gameObject);
}
itemsPool.Clear();
for(int i=0; i< poolSize; i++) // the pool will use itemPrefab as a default
{
Transform item = Instantiate(itemPrefab) as Transform;
item.gameObject.SetActive(false);
item.GetComponent().itemNumber = i;
item.name = "item"+i;
item.parent = table.transform;
itemsPool.Add(item);
}
}
#endregion
}
/* The MIT License (MIT)
Copyright (c) 2014 Orange Labs UK
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using UnityEngine;
using System.Collections;
public class InfiniteItemBehavior : MonoBehaviour {
public UILabel label;
public UISprite backgroundSprite;
public UIPanel panel;
public InfiniteListPopulator listPopulator;
public int itemNumber;
public int itemDataIndex;
public Color spriteColor = new Color();
public Color spritePressedColor = new Color();
private bool isVisible = true;
private BoxCollider thisCollider;
// Use this for initialization
void Start()
{
thisCollider = GetComponent();
transform.localScale = new Vector3(1,1,1); // some weird scaling issues with NGUI
}
void Update()
{
if(Mathf.Abs(listPopulator.draggablePanel.currentMomentum.y) >0)
{
CheckVisibilty();
}
}
public bool verifyVisibility()
{
return(panel.IsVisible(label));
}
void OnClick()
{
listPopulator.itemClicked(itemDataIndex);
}
void OnDrag(Vector2 delta)
{
backgroundSprite.color = spriteColor;
}
void OnPress (bool isDown)
{
listPopulator.itemIsPressed(itemDataIndex,isDown);
// change sprite color
backgroundSprite.color = isDown?spritePressedColor:spriteColor;
}
void CheckVisibilty()
{
bool currentVisibilty = panel.IsVisible(backgroundSprite);
if(currentVisibilty != isVisible)
{
isVisible = currentVisibilty;
thisCollider.enabled = isVisible;
if(!isVisible)
{
StartCoroutine(listPopulator.ItemIsInvisible(itemNumber));
}
}
}
}
/* The MIT License (MIT)
Copyright (c) 2014 Orange Labs UK
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using UnityEngine;
using System.Collections;
public class InfiniteSectionBehavior : MonoBehaviour {
public UILabel label;
public UIPanel panel;
public InfiniteListPopulator listPopulator;
public int itemNumber;
public int itemDataIndex;
public bool isVisible = true;
private BoxCollider thisCollider;
// Use this for initialization
void Start()
{
thisCollider = GetComponent();
transform.localScale = new Vector3(1,1,1);
}
void Update()
{
if(Mathf.Abs(listPopulator.draggablePanel.currentMomentum.y) >0)
{
CheckVisibilty();
}
}
public bool verifyVisibility()
{
return(panel.IsVisible(label));
}
void CheckVisibilty()
{
bool currentVisibilty = panel.IsVisible(label);
if(currentVisibilty != isVisible)
{
isVisible = currentVisibilty;
thisCollider.enabled = isVisible;
if(!isVisible)
{
listPopulator.StartCoroutine(listPopulator.ItemIsInvisible(itemNumber));
}
}
}
}