本文向读者提供SuperScrollView插件内常用Example的演示和代码分析,意在遇到相应开发需求时,可以快速找到对应的Example和代码,并理解基本实现思路
一.ChangeItemHeightDemo
运行时改变item的高度.
代码简述:item展开/收缩时,调用OnItemSizeChanged,并改变item的UI
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace SuperScrollView
{
public class ListItem8 : MonoBehaviour
{
public Text mNameText;
public GameObject mExpandContentRoot;
public Text mClickTip;
public Button mExpandBtn;
int mItemDataIndex = -1;
bool mIsExpand;
public void Init()
{
mExpandBtn.onClick.AddListener( OnExpandBtnClicked );
}
public void OnExpandChanged()
{
RectTransform rt = gameObject.GetComponent<RectTransform>();
if (mIsExpand)
{
rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 284f);
mExpandContentRoot.SetActive(true);
mClickTip.text = "Shrink";
}
else
{
rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 143f);
mExpandContentRoot.SetActive(false);
mClickTip.text = "Expand";
}
}
void OnExpandBtnClicked()
{
ItemData data = DataSourceMgr.Get.GetItemDataByIndex(mItemDataIndex);
if (data == null)
{
return;
}
mIsExpand = !mIsExpand;
data.mIsExpand = mIsExpand;
OnExpandChanged();
LoopListViewItem2 item2 = gameObject.GetComponent<LoopListViewItem2>();
item2.ParentListView.OnItemSizeChanged(item2.ItemIndex);
}
public void SetItemData(ItemData itemData, int itemIndex)
{
mItemDataIndex = itemIndex;
mNameText.text = itemData.mName;
mIsExpand = itemData.mIsExpand;
OnExpandChanged();
}
}
}
二.ChatMsgListViewDemo(Important)
聊天信息展示
代码简述1:item根据头像在左侧或是右侧,分为2个prefab,在OnGetItemByIndex回调中,根据条件itemData.mPersonId == 0,将两个prefab名称传给方法NewListViewItem
LoopListViewItem2 OnGetItemByIndex(LoopListView2 listView, int index)
{
if (index < 0 || index >= ChatMsgDataSourceMgr.Get.TotalItemCount)
{
return null;
}
ChatMsg itemData = ChatMsgDataSourceMgr.Get.GetChatMsgByIndex(index);
if (itemData == null)
{
return null;
}
LoopListViewItem2 item = null;
if (itemData.mPersonId == 0)
{
item = listView.NewListViewItem("ItemPrefab1");
}
else
{
item = listView.NewListViewItem("ItemPrefab2");
}
ListItem4 itemScript = item.GetComponent<ListItem4>();
if (item.IsInitHandlerCalled == false)
{
item.IsInitHandlerCalled = true;
itemScript.Init();
}
itemScript.SetItemData(itemData,index);
return item;
}
代码简述2:将Item分为字符串/图片/等等;根据每种类型,在Item更新UI的时候,设置Item的高度,设置mItemBg的高度和颜色。对于字符串类型的item,Text组件上使用ContentSizeFilter组件,实现高度自适应,得出的高度作为给mItemBg和Item高度赋值的数据来源。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace SuperScrollView
{
public class ListItem4 : MonoBehaviour
{
public Text mMsgText;
public Image mMsgPic;
public Image mIcon;
public Image mItemBg;
public Image mArrow;
public Text mIndexText;
int mItemIndex = -1;
public int ItemIndex
{
get
{
return mItemIndex;
}
}
public void Init()
{
}
public void SetItemData(ChatMsg itemData, int itemIndex)
{
mIndexText.text = itemIndex.ToString();
PersonInfo person = ChatMsgDataSourceMgr.Get.GetPersonInfo(itemData.mPersonId);
mItemIndex = itemIndex;
if(itemData.mMsgType == MsgTypeEnum.Str)
{
mMsgPic.gameObject.SetActive(false);
mMsgText.gameObject.SetActive(true);
mMsgText.text = itemData.mSrtMsg;
mMsgText.GetComponent<ContentSizeFitter>().SetLayoutVertical();
mIcon.sprite = ResManager.Get.GetSpriteByName(person.mHeadIcon);
Vector2 size = mItemBg.GetComponent<RectTransform>().sizeDelta;
size.x = mMsgText.GetComponent<RectTransform>().sizeDelta.x + 20;
size.y = mMsgText.GetComponent<RectTransform>().sizeDelta.y + 20;
mItemBg.GetComponent<RectTransform>().sizeDelta = size;
if(person.mId == 0)
{
mItemBg.color = new Color32(160, 231, 90, 255);
mArrow.color = mItemBg.color;
}
else
{
mItemBg.color = Color.white;
mArrow.color = mItemBg.color;
}
RectTransform tf = gameObject.GetComponent<RectTransform>();
float y = size.y;
if (y < 75)
{
y = 75;
}
tf.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, y);
}
else
{
mMsgPic.gameObject.SetActive(true);
mMsgText.gameObject.SetActive(false);
mMsgPic.sprite = ResManager.Get.GetSpriteByName(itemData.mPicMsgSpriteName);
mMsgPic.SetNativeSize();
mIcon.sprite = ResManager.Get.GetSpriteByName(person.mHeadIcon);
Vector2 size = mItemBg.GetComponent<RectTransform>().sizeDelta;
size.x = mMsgPic.GetComponent<RectTransform>().sizeDelta.x + 20;
size.y = mMsgPic.GetComponent<RectTransform>().sizeDelta.y + 20;
mItemBg.GetComponent<RectTransform>().sizeDelta = size;
if (person.mId == 0)
{
mItemBg.color = new Color32(160, 231, 90, 255);
mArrow.color = mItemBg.color;
}
else
{
mItemBg.color = Color.white;
mArrow.color = mItemBg.color;
}
RectTransform tf = gameObject.GetComponent<RectTransform>();
float y = size.y;
if (y < 75)
{
y = 75;
}
tf.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, y);
}
}
}
}
三.ClickAndLoadMoreDemo
运行时,最后一个item是个button,点击后button变成loading状态,经过一秒,加载更多数据并刷新ScrollView
代码简述:在OnGetItemByIndex中,根据是否为最后一个Item,差异化处理item。对于最后一个Item,处理它未点击,加载中的显示。刷新Item时,获取新的数据后,调用SetListItemCount和RefreshAllShownItem完成ScrollView的刷新。
LoopListViewItem2 OnGetItemByIndex(LoopListView2 listView, int index)
{
if (index < 0)
{
return null;
}
LoopListViewItem2 item = null;
if (index == DataSourceMgr.Get.TotalItemCount)
{
item = listView.NewListViewItem("ItemPrefab0");
if (item.IsInitHandlerCalled == false)
{
item.IsInitHandlerCalled = true;
ListItem11 itemScript0 = item.GetComponent<ListItem11>();
itemScript0.mRootButton.onClick.AddListener(OnLoadMoreBtnClicked);
}
UpdateLoadingTip(item);
return item;
}
ItemData itemData = DataSourceMgr.Get.GetItemDataByIndex(index);
if (itemData == null)
{
return null;
}
item = listView.NewListViewItem("ItemPrefab1");
ListItem2 itemScript = item.GetComponent<ListItem2>();
if (item.IsInitHandlerCalled == false)
{
item.IsInitHandlerCalled = true;
itemScript.Init();
}
itemScript.SetItemData(itemData, index);
return item;
}
void UpdateLoadingTip(LoopListViewItem2 item)
{
if (item == null)
{
return;
}
ListItem11 itemScript0 = item.GetComponent<ListItem11>();
if (itemScript0 == null)
{
return;
}
if (mLoadingTipStatus == LoadingTipStatus.None)
{
itemScript0.mText.text = "Click to Load More";
itemScript0.mWaitingIcon.SetActive(false);
}
else if (mLoadingTipStatus == LoadingTipStatus.WaitLoad)
{
itemScript0.mWaitingIcon.SetActive(true);
itemScript0.mText.text = "Loading ...";
}
}
void OnLoadMoreBtnClicked()
{
if (mLoopListView.ShownItemCount == 0)
{
return;
}
if (mLoadingTipStatus != LoadingTipStatus.None)
{
return;
}
LoopListViewItem2 item = mLoopListView.GetShownItemByItemIndex(DataSourceMgr.Get.TotalItemCount);
if (item == null)
{
return;
}
mLoadingTipStatus = LoadingTipStatus.WaitLoad;
UpdateLoadingTip(item);
DataSourceMgr.Get.RequestLoadMoreDataList(mLoadMoreCount, OnDataSourceLoadMoreFinished);
}
void OnDataSourceLoadMoreFinished()
{
if (mLoopListView.ShownItemCount == 0)
{
return;
}
if (mLoadingTipStatus == LoadingTipStatus.WaitLoad)
{
mLoadingTipStatus = LoadingTipStatus.None;
mLoopListView.SetListItemCount(DataSourceMgr.Get.TotalItemCount + 1, false);
mLoopListView.RefreshAllShownItem();
}
}
public void RequestLoadMoreDataList(int loadCount,System.Action onLoadMoreFinished)
{
mLoadMoreCount = loadCount;
mDataLoadLeftTime = 1;
mOnLoadMoreFinished = onLoadMoreFinished;
mIsWaitLoadingMoreData = true;
}
public void Update()
{
if (mIsWaittingRefreshData)
{
mDataRefreshLeftTime -= Time.deltaTime;
if (mDataRefreshLeftTime <= 0)
{
mIsWaittingRefreshData = false;
DoRefreshDataSource();
if (mOnRefreshFinished != null)
{
mOnRefreshFinished();
}
}
}
if (mIsWaitLoadingMoreData)
{
mDataLoadLeftTime -= Time.deltaTime;
if (mDataLoadLeftTime <= 0)
{
mIsWaitLoadingMoreData = false;
DoLoadMoreDataSource();
if (mOnLoadMoreFinished != null)
{
mOnLoadMoreFinished();
}
}
}
}
四.HorizontalGalleryDemo(Important)
滚动时改变Item的Scale和透明度
代码简述:每帧对alpha和localScale赋值,具体取值参考代码写法
void LateUpdate()
{
mLoopListView.UpdateAllShownItemSnapData();
int count = mLoopListView.ShownItemCount;
for (int i = 0; i < count; ++i)
{
LoopListViewItem2 item = mLoopListView.GetShownItemByIndex(i);
ListItem5 itemScript = item.GetComponent<ListItem5>();
float scale = 1 - Mathf.Abs(item.DistanceWithViewPortSnapCenter) / 700f;
scale = Mathf.Clamp(scale, 0.4f, 1);
itemScript.mContentRootObj.GetComponent<CanvasGroup>().alpha = scale;
itemScript.mContentRootObj.transform.localScale = new Vector3(scale, scale, 1);
}
}
五.PageViewDemo
翻页视图
Editor Setting如下:
代码整体来说有一定难度,有的细节不太好理解,主要原因是使用了不常用的API(SetSnapTargetItemIndex),而且官方文档和代码注释没对这些API做有效解释。遇到翻页开发需求,先一模一样的抄,有必要理解的时候再精细研究。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace SuperScrollView
{
public class DotElem
{
public GameObject mDotElemRoot;
public GameObject mDotSmall;
public GameObject mDotBig;
}
public class PageViewDemoScript : MonoBehaviour
{
public LoopListView2 mLoopListView;
Button mBackButton;
int mPageCount = 5;
public Transform mDotsRootObj;
List<DotElem> mDotElemList = new List<DotElem>();
void Start()
{
InitDots();
LoopListViewInitParam initParam = LoopListViewInitParam.CopyDefaultInitParam();
initParam.mSnapVecThreshold = 99999;
mLoopListView.mOnEndDragAction = OnEndDrag;
mLoopListView.mOnSnapNearestChanged = OnSnapNearestChanged;
mLoopListView.InitListView(mPageCount, OnGetItemByIndex, initParam);
mBackButton = GameObject.Find("ButtonPanel/BackButton").GetComponent<Button>();
mBackButton.onClick.AddListener(OnBackBtnClicked);
}
void InitDots()
{
int childCount = mDotsRootObj.childCount;
for (int i = 0; i < childCount; ++i)
{
Transform tf = mDotsRootObj.GetChild(i);
DotElem elem = new DotElem();
elem.mDotElemRoot = tf.gameObject;
elem.mDotSmall = tf.Find("dotSmall").gameObject;
elem.mDotBig = tf.Find("dotBig").gameObject;
ClickEventListener listener = ClickEventListener.Get(elem.mDotElemRoot);
int index = i;
listener.SetClickEventHandler(delegate (GameObject obj) { OnDotClicked(index); });
mDotElemList.Add(elem);
}
}
void OnDotClicked(int index)
{
int curNearestItemIndex = mLoopListView.CurSnapNearestItemIndex;
if (curNearestItemIndex < 0 || curNearestItemIndex >= mPageCount)
{
return;
}
if(index == curNearestItemIndex)
{
return;
}
mLoopListView.SetSnapTargetItemIndex(index);
}
void UpdateAllDots()
{
int curNearestItemIndex = mLoopListView.CurSnapNearestItemIndex;
if(curNearestItemIndex < 0 || curNearestItemIndex >= mPageCount)
{
return;
}
int count = mDotElemList.Count;
if(curNearestItemIndex >= count)
{
return;
}
for(int i = 0;i<count;++i)
{
DotElem elem = mDotElemList[i];
if(i != curNearestItemIndex)
{
elem.mDotSmall.SetActive(true);
elem.mDotBig.SetActive(false);
}
else
{
elem.mDotSmall.SetActive(false);
elem.mDotBig.SetActive(true);
}
}
}
void OnSnapNearestChanged(LoopListView2 listView, LoopListViewItem2 item)
{
UpdateAllDots();
}
void OnBackBtnClicked()
{
UnityEngine.SceneManagement.SceneManager.LoadScene("Menu");
}
LoopListViewItem2 OnGetItemByIndex(LoopListView2 listView, int pageIndex)
{
if (pageIndex < 0 || pageIndex >= mPageCount)
{
return null;
}
LoopListViewItem2 item = listView.NewListViewItem("ItemPrefab1");
ListItem14 itemScript = item.GetComponent<ListItem14>();
if (item.IsInitHandlerCalled == false)
{
item.IsInitHandlerCalled = true;
itemScript.Init();
}
List<ListItem14Elem> elemList = itemScript.mElemItemList;
int count = elemList.Count;
int picBeginIndex = pageIndex * count;
int i = 0;
for(;i< count;++i)
{
ItemData itemData = DataSourceMgr.Get.GetItemDataByIndex(picBeginIndex+i);
if(itemData == null)
{
break;
}
ListItem14Elem elem = elemList[i];
elem.mRootObj.SetActive(true);
elem.mIcon.sprite = ResManager.Get.GetSpriteByName(itemData.mIcon);
elem.mName.text = itemData.mName;
}
if(i < count)
{
for(;i< count;++i)
{
elemList[i].mRootObj.SetActive(false);
}
}
return item;
}
void OnEndDrag()
{
float vec = mLoopListView.ScrollRect.velocity.x;
int curNearestItemIndex = mLoopListView.CurSnapNearestItemIndex;
LoopListViewItem2 item = mLoopListView.GetShownItemByItemIndex(curNearestItemIndex);
if(item == null)
{
mLoopListView.ClearSnapData();
return;
}
if (Mathf.Abs(vec) < 50f)
{
mLoopListView.SetSnapTargetItemIndex(curNearestItemIndex);
return;
}
Vector3 pos = mLoopListView.GetItemCornerPosInViewPort(item, ItemCornerEnum.LeftTop);
if(pos.x > 0)
{
if (vec > 0)
{
mLoopListView.SetSnapTargetItemIndex(curNearestItemIndex - 1);
}
else
{
mLoopListView.SetSnapTargetItemIndex(curNearestItemIndex);
}
}
else if (pos.x < 0)
{
if (vec > 0)
{
mLoopListView.SetSnapTargetItemIndex(curNearestItemIndex);
}
else
{
mLoopListView.SetSnapTargetItemIndex(curNearestItemIndex+1);
}
}
else
{
if (vec > 0)
{
mLoopListView.SetSnapTargetItemIndex(curNearestItemIndex-1);
}
else
{
mLoopListView.SetSnapTargetItemIndex(curNearestItemIndex + 1);
}
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace SuperScrollView
{
public class ListItem14Elem
{
public GameObject mRootObj;
public Image mIcon;
public Text mName;
}
public class ListItem14 : MonoBehaviour
{
public List<ListItem14Elem> mElemItemList = new List<ListItem14Elem>();
public void Init()
{
int childCount = transform.childCount;
for(int i= 0;i<childCount;++i)
{
Transform tf = transform.GetChild(i);
ListItem14Elem elem = new ListItem14Elem();
elem.mRootObj = tf.gameObject;
elem.mIcon = tf.Find("ItemIcon").GetComponent<Image>();
elem.mName = tf.Find("ItemIcon/name").GetComponent<Text>();
mElemItemList.Add(elem);
}
}
}
}
六.PullAndLoadMoreDemo(Important)
滚动到最后一个Item后,通过拖拽加载更多Item
代码简述:在OnGetItemByIndex中,根据是否为最后一个Item,差异化处理item。对于最后一个Item,处理它OnDraging事件和OnEndDrag事件的UI。刷新Item时,获取新的数据后,调用SetListItemCount和RefreshAllShownItem完成ScrollView的刷新。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace SuperScrollView
{
public class PullToLoadMoreDemoScript : MonoBehaviour
{
public LoopListView2 mLoopListView;
LoadingTipStatus mLoadingTipStatus = LoadingTipStatus.None;
float mLoadingTipItemHeight = 100;
int mLoadMoreCount = 20;
Button mScrollToButton;
InputField mScrollToInput;
Button mBackButton;
// Use this for initialization
void Start()
{
// totalItemCount +1 because the last "load more" banner is also a item.
mLoopListView.InitListView(DataSourceMgr.Get.TotalItemCount + 1, OnGetItemByIndex);
mLoopListView.mOnDragingAction = OnDraging;
mLoopListView.mOnEndDragAction = OnEndDrag;
mScrollToButton = GameObject.Find("ButtonPanel/buttonGroup2/ScrollToButton").GetComponent<Button>();
mScrollToInput = GameObject.Find("ButtonPanel/buttonGroup2/ScrollToInputField").GetComponent<InputField>();
mBackButton = GameObject.Find("ButtonPanel/BackButton").GetComponent<Button>();
}
LoopListViewItem2 OnGetItemByIndex(LoopListView2 listView, int index)
{
if (index < 0)
{
return null;
}
LoopListViewItem2 item = null;
if (index == DataSourceMgr.Get.TotalItemCount)
{
item = listView.NewListViewItem("ItemPrefab0");
UpdateLoadingTip(item);
return item;
}
ItemData itemData = DataSourceMgr.Get.GetItemDataByIndex(index);
if (itemData == null)
{
return null;
}
item = listView.NewListViewItem("ItemPrefab1");
ListItem2 itemScript = item.GetComponent<ListItem2>();
if (item.IsInitHandlerCalled == false)
{
item.IsInitHandlerCalled = true;
itemScript.Init();
}
if(index == DataSourceMgr.Get.TotalItemCount -1)
{
item.Padding = 0;
}
itemScript.SetItemData(itemData, index);
return item;
}
void UpdateLoadingTip(LoopListViewItem2 item)
{
if (item == null)
{
return;
}
ListItem0 itemScript0 = item.GetComponent<ListItem0>();
if(itemScript0 == null)
{
return;
}
if (mLoadingTipStatus == LoadingTipStatus.None)
{
itemScript0.mRoot.SetActive(false);
item.CachedRectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0);
}
else if (mLoadingTipStatus == LoadingTipStatus.WaitRelease)
{
itemScript0.mRoot.SetActive(true);
itemScript0.mText.text = "Release to Load More";
itemScript0.mArrow.SetActive(true);
itemScript0.mWaitingIcon.SetActive(false);
item.CachedRectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, mLoadingTipItemHeight);
}
else if (mLoadingTipStatus == LoadingTipStatus.WaitLoad)
{
itemScript0.mRoot.SetActive(true);
itemScript0.mArrow.SetActive(false);
itemScript0.mWaitingIcon.SetActive(true);
itemScript0.mText.text = "Loading ...";
item.CachedRectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, mLoadingTipItemHeight);
}
}
void OnDraging()
{
if (mLoopListView.ShownItemCount == 0)
{
return;
}
if (mLoadingTipStatus != LoadingTipStatus.None && mLoadingTipStatus != LoadingTipStatus.WaitRelease)
{
return;
}
LoopListViewItem2 item = mLoopListView.GetShownItemByItemIndex(DataSourceMgr.Get.TotalItemCount);
if (item == null)
{
return;
}
LoopListViewItem2 item1 = mLoopListView.GetShownItemByItemIndex(DataSourceMgr.Get.TotalItemCount-1);
if (item1 == null)
{
return;
}
float y = mLoopListView.GetItemCornerPosInViewPort(item1,ItemCornerEnum.LeftBottom).y;
if(y + mLoopListView.ViewPortSize >= mLoadingTipItemHeight)
{
if (mLoadingTipStatus != LoadingTipStatus.None)
{
return;
}
mLoadingTipStatus = LoadingTipStatus.WaitRelease;
UpdateLoadingTip(item);
}
else
{
if (mLoadingTipStatus != LoadingTipStatus.WaitRelease)
{
return;
}
mLoadingTipStatus = LoadingTipStatus.None;
UpdateLoadingTip(item);
}
}
void OnEndDrag()
{
if (mLoopListView.ShownItemCount == 0)
{
return;
}
if (mLoadingTipStatus != LoadingTipStatus.None && mLoadingTipStatus != LoadingTipStatus.WaitRelease)
{
return;
}
LoopListViewItem2 item = mLoopListView.GetShownItemByItemIndex(DataSourceMgr.Get.TotalItemCount);
if (item == null)
{
return;
}
mLoopListView.OnItemSizeChanged(item.ItemIndex);
if (mLoadingTipStatus != LoadingTipStatus.WaitRelease)
{
return;
}
mLoadingTipStatus = LoadingTipStatus.WaitLoad;
UpdateLoadingTip(item);
DataSourceMgr.Get.RequestLoadMoreDataList(mLoadMoreCount, OnDataSourceLoadMoreFinished);
}
void OnDataSourceLoadMoreFinished()
{
if (mLoopListView.ShownItemCount == 0)
{
return;
}
if (mLoadingTipStatus == LoadingTipStatus.WaitLoad)
{
mLoadingTipStatus = LoadingTipStatus.None;
mLoopListView.SetListItemCount(DataSourceMgr.Get.TotalItemCount + 1, false);
mLoopListView.RefreshAllShownItem();
}
}
}
}
七.SpinDatePickerDemo
3个ScrollView都可以滚动,吸附,取中间值,拼凑出一个最终的值的Demo
代码量不多也不难理解,比较值得一提的是将处理颜色的方法赋值给mOnSnapNearestChanged,在吸附后自动调用。另外OnGetItemByIndexForMonth和通用的回调不一样,不需要进行return判断。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace SuperScrollView
{
public class SpinDatePickerDemoScript : MonoBehaviour
{
public LoopListView2 mLoopListViewMonth;
public LoopListView2 mLoopListViewDay;
public LoopListView2 mLoopListViewHour;
public Button mBackButton;
static int[] mMonthDayCountArray = new int[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static string[] mMonthNameArray = new string[]
{
"Jan.",
"Feb.",
"Mar.",
"Apr.",
"May.",
"Jun.",
"Jul.",
"Aug.",
"Sep.",
"Oct.",
"Nov.",
"Dec.",
};
int mCurSelectedMonth = 2;
int mCurSelectedDay = 2;
int mCurSelectedHour = 2;
public int CurSelectedMonth
{
get { return mCurSelectedMonth; }
}
public int CurSelectedDay
{
get { return mCurSelectedDay; }
}
public int CurSelectedHour
{
get { return mCurSelectedHour; }
}
// Use this for initialization
void Start()
{
//set all snap callback.
mLoopListViewMonth.mOnSnapNearestChanged = OnMonthSnapTargetChanged;
mLoopListViewDay.mOnSnapNearestChanged = OnDaySnapTargetChanged;
mLoopListViewHour.mOnSnapNearestChanged = OnHourSnapTargetChanged;
//init all superListView.
mLoopListViewMonth.InitListView(-1, OnGetItemByIndexForMonth);
mLoopListViewDay.InitListView(-1, OnGetItemByIndexForDay);
mLoopListViewHour.InitListView(-1, OnGetItemByIndexForHour);
mLoopListViewMonth.mOnSnapItemFinished = OnMonthSnapTargetFinished;
mBackButton.onClick.AddListener(OnBackBtnClicked);
}
void OnBackBtnClicked()
{
UnityEngine.SceneManagement.SceneManager.LoadScene("Menu");
}
LoopListViewItem2 OnGetItemByIndexForHour(LoopListView2 listView, int index)
{
LoopListViewItem2 item = listView.NewListViewItem("ItemPrefab1");
ListItem7 itemScript = item.GetComponent<ListItem7>();
if (item.IsInitHandlerCalled == false)
{
item.IsInitHandlerCalled = true;
itemScript.Init();
}
int firstItemVal = 1;
int itemCount = 24;
int val = 0;
if(index >= 0)
{
val = index % itemCount;
}
else
{
val = itemCount + ((index + 1) % itemCount) - 1;
}
val = val + firstItemVal;
itemScript.Value = val;
itemScript.mText.text = val.ToString();
return item;
}
LoopListViewItem2 OnGetItemByIndexForMonth(LoopListView2 listView, int index)
{
LoopListViewItem2 item = listView.NewListViewItem("ItemPrefab1");
ListItem7 itemScript = item.GetComponent<ListItem7>();
if (item.IsInitHandlerCalled == false)
{
item.IsInitHandlerCalled = true;
itemScript.Init();
}
int firstItemVal = 1;
int itemCount = 12;
int val = 0;
if (index >= 0)
{
val = index % itemCount;
}
else
{
val = itemCount + ((index+1) % itemCount)-1;
}
val = val + firstItemVal;
itemScript.Value = val;
itemScript.mText.text = mMonthNameArray[val-1];
return item;
}
LoopListViewItem2 OnGetItemByIndexForDay(LoopListView2 listView, int index)
{
LoopListViewItem2 item = listView.NewListViewItem("ItemPrefab1");
ListItem7 itemScript = item.GetComponent<ListItem7>();
if (item.IsInitHandlerCalled == false)
{
item.IsInitHandlerCalled = true;
itemScript.Init();
}
int firstItemVal = 1;
int itemCount = mMonthDayCountArray[mCurSelectedMonth-1];
int val = 0;
if (index >= 0)
{
val = index % itemCount;
}
else
{
val = itemCount + ((index + 1) % itemCount) - 1;
}
val = val + firstItemVal;
itemScript.Value = val;
itemScript.mText.text = val.ToString();
return item;
}
void OnMonthSnapTargetChanged(LoopListView2 listView, LoopListViewItem2 item)
{
int index = listView.GetIndexInShownItemList(item);
if (index < 0)
{
return;
}
ListItem7 itemScript = item.GetComponent<ListItem7>();
mCurSelectedMonth = itemScript.Value;
OnListViewSnapTargetChanged(listView, index);
}
void OnDaySnapTargetChanged(LoopListView2 listView, LoopListViewItem2 item)
{
int index = listView.GetIndexInShownItemList(item);
if (index < 0)
{
return;
}
ListItem7 itemScript = item.GetComponent<ListItem7>();
mCurSelectedDay = itemScript.Value;
OnListViewSnapTargetChanged(listView, index);
}
void OnHourSnapTargetChanged(LoopListView2 listView, LoopListViewItem2 item)
{
int index = listView.GetIndexInShownItemList(item);
if (index < 0)
{
return;
}
ListItem7 itemScript = item.GetComponent<ListItem7>();
mCurSelectedHour = itemScript.Value;
OnListViewSnapTargetChanged(listView, index);
}
void OnMonthSnapTargetFinished(LoopListView2 listView, LoopListViewItem2 item)
{
LoopListViewItem2 item0 = mLoopListViewDay.GetShownItemByIndex(0);
ListItem7 itemScript = item0.GetComponent<ListItem7>();
int index = itemScript.Value - 1;
mLoopListViewDay.RefreshAllShownItemWithFirstIndex(index);
}
void OnListViewSnapTargetChanged(LoopListView2 listView, int targetIndex)
{
int count = listView.ShownItemCount;
for (int i = 0; i < count; ++i)
{
LoopListViewItem2 item2 = listView.GetShownItemByIndex(i);
ListItem7 itemScript = item2.GetComponent<ListItem7>();
if (i == targetIndex)
{
itemScript.mText.color = Color.red;
}
else
{
itemScript.mText.color = Color.black;
}
}
}
}
}
八.TreeViewDemo(Important)
Item可展开二级子Item的情况,这种情况是比较常用的,比如交易行,图鉴系统等。
代码概述:Item分为2种Prefab,一级Item和子Item,使用管理类TreeViewItemCountMgr对Item进行管理;在OnGetItemByIndex方法内,通过方法mTreeItemCountMgr.QueryTreeItemByTotalIndex获取到Item的Data,在进行prefab的分类处理。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace SuperScrollView
{
public class TreeViewDemoScript : MonoBehaviour
{
public LoopListView2 mLoopListView;
Button mScrollToButton;
Button mExpandAllButton;
Button mCollapseAllButton;
InputField mScrollToInputItem;
InputField mScrollToInputChild;
// an helper class for TreeView item showing.
TreeViewItemCountMgr mTreeItemCountMgr = new TreeViewItemCountMgr();
// Use this for initialization
void Start()
{
int count = TreeViewDataSourceMgr.Get.TreeViewItemCount;
//tells mTreeItemCountMgr there are how many TreeItems and every TreeItem has how many ChildItems.
for (int i = 0; i < count; ++i)
{
int childCount = TreeViewDataSourceMgr.Get.GetItemDataByIndex(i).ChildCount;
//second param "true" tells mTreeItemCountMgr this TreeItem is in expand status, that is to say all its children are showing.
mTreeItemCountMgr.AddTreeItem(childCount, true);
}
//initialize the InitListView
//mTreeItemCountMgr.GetTotalItemAndChildCount() return the total items count in the TreeView, include all TreeItems and all TreeChildItems.
mLoopListView.InitListView(mTreeItemCountMgr.GetTotalItemAndChildCount(), OnGetItemByIndex);
mExpandAllButton = GameObject.Find("ButtonPanel/buttonGroup1/ExpandAllButton").GetComponent<Button>();
mScrollToButton = GameObject.Find("ButtonPanel/buttonGroup2/ScrollToButton").GetComponent<Button>();
mCollapseAllButton = GameObject.Find("ButtonPanel/buttonGroup3/CollapseAllButton").GetComponent<Button>();
mScrollToInputItem = GameObject.Find("ButtonPanel/buttonGroup2/ScrollToInputFieldItem").GetComponent<InputField>();
mScrollToInputChild = GameObject.Find("ButtonPanel/buttonGroup2/ScrollToInputFieldChild").GetComponent<InputField>();
mExpandAllButton.onClick.AddListener(OnExpandAllBtnClicked);
mCollapseAllButton.onClick.AddListener(OnCollapseAllBtnClicked);
}
//when a TreeItem or TreeChildItem is getting in the scrollrect viewport,
//this method will be called with the item’ index as a parameter,
//to let you create the item and update its content.
LoopListViewItem2 OnGetItemByIndex(LoopListView2 listView, int index)
{
if (index < 0)
{
return null;
}
/*to check the index'th item is a TreeItem or a TreeChildItem.for example,
0 TreeItem0
1 TreeChildItem0_0
2 TreeChildItem0_1
3 TreeChildItem0_2
4 TreeChildItem0_3
5 TreeItem1
6 TreeChildItem1_0
7 TreeChildItem1_1
8 TreeChildItem1_2
9 TreeItem2
10 TreeChildItem2_0
11 TreeChildItem2_1
12 TreeChildItem2_2
the first column value is the param 'index', for example, if index is 1,
then we should return TreeChildItem0_0 to SuperScrollView, and if index is 5,
then we should return TreeItem1 to SuperScrollView
*/
TreeViewItemCountData countData = mTreeItemCountMgr.QueryTreeItemByTotalIndex(index);
if(countData == null)
{
return null;
}
int treeItemIndex = countData.mTreeItemIndex;
TreeViewItemData treeViewItemData = TreeViewDataSourceMgr.Get.GetItemDataByIndex(treeItemIndex);
if (countData.IsChild(index) == false)// if is a TreeItem
{
//get a new TreeItem
LoopListViewItem2 item = listView.NewListViewItem("ItemPrefab1");
ListItem12 itemScript = item.GetComponent<ListItem12>();
if (item.IsInitHandlerCalled == false)
{
item.IsInitHandlerCalled = true;
itemScript.Init();
itemScript.SetClickCallBack(this.OnExpandClicked);
}
//update the TreeItem's content
itemScript.mText.text = treeViewItemData.mName;
itemScript.SetItemData(treeItemIndex, countData.mIsExpand);
return item;
}
else // if is a TreeChildItem
{
//childIndex is from 0 to ChildCount.
//for example, TreeChildItem0_0 is the 0'th child of TreeItem0
//and TreeChildItem1_2 is the 2'th child of TreeItem1
int childIndex = countData.GetChildIndex(index);
ItemData itemData = treeViewItemData.GetChild(childIndex);
if (itemData == null)
{
return null;
}
//get a new TreeChildItem
LoopListViewItem2 item = listView.NewListViewItem("ItemPrefab2");
ListItem13 itemScript = item.GetComponent<ListItem13>();
if (item.IsInitHandlerCalled == false)
{
item.IsInitHandlerCalled = true;
itemScript.Init();
}
//update the TreeChildItem's content
itemScript.SetItemData(itemData, treeItemIndex, childIndex);
return item;
}
}
public void OnExpandClicked(int index)
{
mTreeItemCountMgr.ToggleItemExpand(index);
mLoopListView.SetListItemCount(mTreeItemCountMgr.GetTotalItemAndChildCount(),false);
mLoopListView.RefreshAllShownItem();
}
void OnExpandAllBtnClicked()
{
int count = mTreeItemCountMgr.TreeViewItemCount;
for (int i = 0; i < count; ++i)
{
mTreeItemCountMgr.SetItemExpand(i, true);
}
mLoopListView.SetListItemCount(mTreeItemCountMgr.GetTotalItemAndChildCount(), false);
mLoopListView.RefreshAllShownItem();
}
void OnCollapseAllBtnClicked()
{
int count = mTreeItemCountMgr.TreeViewItemCount;
for (int i = 0; i < count; ++i)
{
mTreeItemCountMgr.SetItemExpand(i, false);
}
mLoopListView.SetListItemCount(mTreeItemCountMgr.GetTotalItemAndChildCount(), false);
mLoopListView.RefreshAllShownItem();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SuperScrollView
{
public class TreeViewItemCountData
{
public int mTreeItemIndex = 0;
public int mChildCount = 0;
public bool mIsExpand = true;
public int mBeginIndex = 0;
public int mEndIndex = 0;
public bool IsChild(int index)
{
return (index != mBeginIndex);
}
public int GetChildIndex(int index)
{
if(IsChild(index) == false)
{
return -1;
}
return (index - mBeginIndex - 1);
}
}
public class TreeViewItemCountMgr
{
List<TreeViewItemCountData> mTreeItemDataList = new List<TreeViewItemCountData>();
TreeViewItemCountData mLastQueryResult = null;
bool mIsDirty = true;
public void AddTreeItem(int count, bool isExpand)
{
TreeViewItemCountData data = new TreeViewItemCountData();
data.mTreeItemIndex = mTreeItemDataList.Count;
data.mChildCount = count;
data.mIsExpand = isExpand;
mTreeItemDataList.Add(data);
mIsDirty = true;
}
public void Clear()
{
mTreeItemDataList.Clear();
mLastQueryResult = null;
mIsDirty = true;
}
public TreeViewItemCountData GetTreeItem(int treeIndex)
{
if (treeIndex < 0 || treeIndex >= mTreeItemDataList.Count)
{
return null;
}
return mTreeItemDataList[treeIndex];
}
public void SetItemChildCount(int treeIndex, int count)
{
if (treeIndex < 0 || treeIndex >= mTreeItemDataList.Count)
{
return;
}
mIsDirty = true;
TreeViewItemCountData data = mTreeItemDataList[treeIndex];
data.mChildCount = count;
}
public void SetItemExpand(int treeIndex, bool isExpand)
{
if (treeIndex < 0 || treeIndex >= mTreeItemDataList.Count)
{
return;
}
mIsDirty = true;
TreeViewItemCountData data = mTreeItemDataList[treeIndex];
data.mIsExpand = isExpand;
}
public void ToggleItemExpand(int treeIndex)
{
if (treeIndex < 0 || treeIndex >= mTreeItemDataList.Count)
{
return;
}
mIsDirty = true;
TreeViewItemCountData data = mTreeItemDataList[treeIndex];
data.mIsExpand = !data.mIsExpand;
}
public bool IsTreeItemExpand(int treeIndex)
{
TreeViewItemCountData data = GetTreeItem(treeIndex);
if (data == null)
{
return false;
}
return data.mIsExpand;
}
void UpdateAllTreeItemDataIndex()
{
if (mIsDirty == false)
{
return;
}
mLastQueryResult = null;
mIsDirty = false;
int count = mTreeItemDataList.Count;
if (count == 0)
{
return;
}
TreeViewItemCountData data0 = mTreeItemDataList[0];
data0.mBeginIndex = 0;
data0.mEndIndex = (data0.mIsExpand ? data0.mChildCount : 0);
int curEnd = data0.mEndIndex;
for (int i = 1; i < count; ++i)
{
TreeViewItemCountData data = mTreeItemDataList[i];
data.mBeginIndex = curEnd + 1;
data.mEndIndex = data.mBeginIndex + (data.mIsExpand ? data.mChildCount : 0);
curEnd = data.mEndIndex;
}
}
public int TreeViewItemCount
{
get
{
return mTreeItemDataList.Count;
}
}
public int GetTotalItemAndChildCount()
{
int count = mTreeItemDataList.Count;
if (count == 0)
{
return 0;
}
UpdateAllTreeItemDataIndex();
return mTreeItemDataList[count - 1].mEndIndex + 1;
}
public TreeViewItemCountData QueryTreeItemByTotalIndex(int totalIndex)
{
if (totalIndex < 0)
{
return null;
}
int count = mTreeItemDataList.Count;
if (count == 0)
{
return null;
}
UpdateAllTreeItemDataIndex();
if (mLastQueryResult != null)
{
if (mLastQueryResult.mBeginIndex <= totalIndex && mLastQueryResult.mEndIndex >= totalIndex)
{
return mLastQueryResult;
}
}
int low = 0;
int high = count - 1;
TreeViewItemCountData data = null;
while (low <= high)
{
int mid = (low + high) / 2;
data = mTreeItemDataList[mid];
if (data.mBeginIndex <= totalIndex && data.mEndIndex >= totalIndex)
{
mLastQueryResult = data;
return data;
}
else if (totalIndex > data.mEndIndex)
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
return null;
}
}
}