UGUI:循环滚动框 可设置排列方向
以前写的一个UGUI下的循环滚动框,前段时间用的时候发现不能支持scrollview从下往上滑的模式,于是就做了修改,加上从下往上和从右往左。
通过设置StartConner枚举值设定滑动方式。
除了滑动方式的修改,还修改了根据item的pivot设定来计算排列坐标,以前的item pivot只能设定为0.5 0.5,现在怎么设都可以了。
2017/09/03 做了一些修改。
获取ScrollRect的四角坐标由RectTransform.rect.size的计算改为GetWorldConrners
在ResetChildPosition的时候 先将Grid的尺寸设为0
在RefreshChildData的时候 将Grid的尺寸更新到最大尺寸
public class GridAndLoop : MonoBehaviour
{
///
/// 设置Item内容的委托
///
/// Item对象
/// Item在Grid中的序号
/// 当前Item在List中的序号
public delegate void OnInitializeItem(GameObject item, int wrapIndex, int realIndex);
///
/// 排列方式枚举
///
public enum ArrangeType
{
Horizontal = 0, //水平排列
Vertical = 1, //垂直排列
}
public enum StartConner
{
UpLeft,
UpRight,
LowLeft,
LowRight,
}
///
/// Item的尺寸
///
public int cell_x = 100, cell_y = 100;
///
/// 是否隐藏裁剪部分
///
public bool cullContent = true;
///
/// Item最小序号
///
public int minIndex = 0;
///
/// Item最大序号
///
public int maxIndex = 0;
///
/// 排列方式
///
public ArrangeType arrangeType = ArrangeType.Horizontal;
public StartConner startConner = StartConner.UpLeft;
///
/// 行列个数 0表示1列
///
public int ConstraintCount = 0;
///
/// 设置Item的委托
///
public OnInitializeItem onInitializeItem;
///
///当前对象
///
Transform mTrans;
///
/// 当前RectTransform对象
///
RectTransform mRTrans;
///
/// ScrollRect
///
ScrollRect mScroll;
///
/// 滚动方向
///
bool mHorizontal;
Vector2 mDirect = Vector2.one;
///
/// 元素链表
///
List mChild = new List();
///
/// 显示区域长度或高度的一半
///
float extents = 0;
Vector2 SR_size = Vector2.zero;//SrollRect的尺寸
Vector3[] conners = new Vector3[4];//ScrollRect四角的世界坐标
Vector2 startPos; //ScrollRect的初始位置
bool isStart = false;
void Start()
{
if (isStart) return;
isStart = true;
InitList();
}
int sortByName(Transform a, Transform b) { return string.Compare(a.name, b.name); }
///
/// 初始化mChild链表
///
void InitList()
{
int i, ChildCount;
InitValue();
mChild.Clear();
for (i = 0, ChildCount = mTrans.childCount; i < ChildCount; i++)
mChild.Add(mTrans.GetChild(i));
ResetChildPosition();
// mChild.Sort(sortByName);//按照Item名字排序
}
void InitValue()
{
if (ConstraintCount <= 0)
ConstraintCount = 1;
if (minIndex > maxIndex) minIndex = maxIndex;
mTrans = transform;
mRTrans = transform.GetComponent();
mScroll = transform.GetComponentInParent();
mHorizontal = mScroll.horizontal;
SR_size = transform.parent.GetComponent().rect.size;
(mScroll.transform as RectTransform).GetWorldCorners(conners);
if (startConner == StartConner.UpLeft)
{
mRTrans.pivot = new Vector2(0, 1);//设置grid的中心在左上角 为了适配尺寸用
mDirect.x = 1; //初始化排列方向 x轴正方向 y轴负方向 即 向左和向下 用来算realIndex和排列坐标用
mDirect.y = -1;
}
else if (startConner == StartConner.UpRight)
{
mRTrans.pivot = new Vector2(1, 0);
mDirect.x = -1;
mDirect.y = -1;
}
else if (startConner == StartConner.LowLeft)
{
mRTrans.pivot = new Vector2(0, 0);
mDirect.x = 1;
mDirect.y = 1;
}
else if (startConner == StartConner.LowRight)
{
mRTrans.pivot = new Vector2(1, 0);
mDirect.x = -1;
mDirect.y = 1;
}
mScroll.onValueChanged.AddListener(delegate { WrapContent(); });//添加滚动事件回调
startPos = mTrans.localPosition;
}
//初始化各Item的坐标
[ContextMenu("RePosition")]
public virtual void RePosition()
{
if (!isStart) Start();
InitList();
}
void Update()
{
if (Application.isPlaying) enabled = false;
// RePosition();
}
void ResetChildPosition()
{
int rows = 1, cols = 1;
int i;
int imax = mChild.Count;//Item元素数量
//初始化行列数
if (arrangeType == ArrangeType.Vertical) //垂直排列 则适应行数
{
rows = ConstraintCount;
cols = (int)Mathf.Ceil((float)imax / (float)rows);
extents = (float)(cols * cell_x) * 0.5f;
}
else if (arrangeType == ArrangeType.Horizontal) //水平排列则适应列数
{
cols = ConstraintCount;
rows = (int)Mathf.Ceil((float)imax / (float)cols);
extents = (float)(rows * cell_y) * 0.5f;
}
mRTrans.sizeDelta = Vector2.zero;
for (i = 0; i < imax; i++)
{
RectTransform temp = mChild[i] as RectTransform;
if (startConner == StartConner.UpLeft)
{
temp.anchorMin = new Vector2(0, 1);
temp.anchorMax = new Vector2(0, 1);
}
else if (startConner == StartConner.UpRight)
{
temp.anchorMin = Vector2.one;
temp.anchorMax = Vector2.one;
}
else if (startConner == StartConner.LowLeft)
{
temp.anchorMin = Vector2.zero;
temp.anchorMax = Vector2.zero;
}
else if (startConner == StartConner.LowRight)
{
temp.anchorMin = new Vector2(1, 0);
temp.anchorMax = new Vector2(1, 0);
}
Vector2 startAxis = new Vector2(cell_x * temp.pivot.x * mDirect.x, cell_y * temp.pivot.y * mDirect.y);//起始位置
int x = 0, y = 0;//行列号
if (arrangeType == ArrangeType.Horizontal) { x = i / cols; y = i % cols; }
else if (arrangeType == ArrangeType.Vertical) { x = i % rows; y = i / rows; }
temp.localPosition = new Vector2(startAxis.x + y * cell_x * mDirect.x, startAxis.y + x * cell_y * mDirect.y);
if ((minIndex == maxIndex && maxIndex != 0 ) || (i >= minIndex && i < maxIndex))
{
// cullContent = true;
temp.gameObject.SetActive(true);
UpdateRectsize(temp.localPosition, (temp as RectTransform).pivot);//更新panel的尺寸
UpdateItem(temp, i, i);
}
else
{
temp.gameObject.SetActive(false);
//cullContent = temp.gameObject.activeSelf;//如果预制Item数超过maxIndex则将超过部分隐藏 并 设置cullCintent为ufalse 并且不再更新 panel尺寸
}
}
}
///
/// ScrollRect复位
///
public void ResetPosition()
{
if (!isStart) Start();
mTrans.localPosition = startPos;
ResetChildPosition();
}
public void SetMinMaxIndex(int min, int max)
{
if (!isStart) Start();
minIndex = min;
maxIndex = max;
}
///
/// 刷新Item数据
///
public void RefreshChildData()
{
if (!isStart) Start();
for (int i = 0, imax = mChild.Count; i < imax; ++i)
{
Transform t = mChild[i];
int realIndex = getRealIndex(t.localPosition);
if ((minIndex == maxIndex && maxIndex != 0) || (realIndex >= minIndex && realIndex < maxIndex))
{
t.gameObject.SetActive(true);
UpdateItem(t, i, realIndex);
}
else
{
t.gameObject.SetActive(false);
}
}
UpdateRectsizeToMax();
}
///
/// 获取一个真实序号对应的locl坐标
///
///
///
public Vector3 getRealShouldPos(int realIndex)
{
if (!isStart) Start();
if (mChild.Count == 0)
{
RectTransform recttrans = mChild[0] as RectTransform;
return Vector3.zero + new Vector3(recttrans.pivot.x * cell_x, recttrans.pivot.y * cell_y, 0);
}
Vector3 pos = mChild[0].localPosition;
int realIndex0 = getRealIndex(pos);
if (mHorizontal)
{
pos.x = pos.x + (realIndex - realIndex0) * cell_x * mDirect.x;
}
else
{
pos.y = pos.y + (realIndex - realIndex0) * cell_y * mDirect.y;
}
return pos;
}
///
/// 根据真实序号获取对应的序号
///
///
///
public int getIndexWithRealIndex(int realIndex)
{
if (!isStart) Start();
int index = realIndex % mChild.Count;
return index;
}
///
/// 根据一个真实序号调整当前所有Item的坐标
///
///
public void WrapChildPosWithRealIndex(int realIndex)
{
if (!isStart) Start();
int index = getIndexWithRealIndex(realIndex);
Vector3 targetPos = getRealShouldPos(realIndex);
Vector3 offset = targetPos - mChild[index].localPosition;
for (int i = 0, Imax = mChild.Count; i < Imax; i++)
{
Vector3 pos = mChild[i].localPosition;
pos += offset;
mChild[i].localPosition = pos;
}
WrapContent();
RefreshChildData();
}
///
/// 更新panel的尺寸
///
///
void UpdateRectsize(Vector2 pos, Vector2 pivot)
{
if (arrangeType == ArrangeType.Vertical)
{
float offset = mDirect.x * pos.x;
if (offset < 0) return;
Vector2 size = Vector2.zero;
size.x = offset + cell_x * pivot.x;
size.y = ConstraintCount * cell_y;
mRTrans.sizeDelta = size;
}
else
{
float offset = mDirect.y * pos.y;
if (offset < 0) return;
Vector2 size = Vector2.zero;
size.x = ConstraintCount * cell_x;
size.y = offset + cell_y * pivot.y;
mRTrans.sizeDelta = size;
}
}
///
/// 更新panel尺寸为最大尺寸
///
void UpdateRectsizeToMax()
{
if (arrangeType == ArrangeType.Vertical)
{
Vector2 size = Vector2.zero;
size.x = cell_x * maxIndex;
size.y = ConstraintCount * cell_y;
mRTrans.sizeDelta = size;
}
else
{
Vector2 size = Vector2.zero;
size.x = ConstraintCount * cell_x;
size.y = cell_y * maxIndex;
mRTrans.sizeDelta = size;
}
}
//Vector2 calculatePos(Vector2 world,Vector2 target,Vector2 lcal)
//{
// Vector2 temp = world - target;
// temp.x /= (target.x/lcal.x);
// temp.y /= (target.y/lcal.y);
// return temp;
//}
int getRealIndex(Vector2 pos)//计算realindex
{
int x = (int)Mathf.Ceil(mDirect.y * pos.y / cell_y) - 1; //行号
int y = (int)Mathf.Ceil(mDirect.x * pos.x / cell_x) - 1; //列号
int realIndex;
if (arrangeType == ArrangeType.Horizontal)
{
realIndex = x * ConstraintCount + y;
}
else
{
realIndex = x + ConstraintCount * y;
}
return realIndex;
}
void WrapContent()
{
Vector3[] conner_local = new Vector3[4];
for (int i = 0; i < 4; i++)
{
conner_local[i] = mTrans.InverseTransformPoint(conners[i]);
}
//计算ScrollRect的中心坐标 相对于this的坐标
Vector2 center = (conner_local[0] + conner_local[2]) / 2f;
if (mHorizontal)
{
float min = conner_local[0].x - cell_x;//显示区域
float max = conner_local[2].x + cell_x;
for (int i = 0, imax = mChild.Count; i < imax; i++)
{
Transform temp = mChild[i];
float distance = temp.localPosition.x - center.x;
if (distance < -extents)
{
Vector2 pos = temp.localPosition;
pos.x += extents * 2f;
int realIndex = getRealIndex(pos);
if ((minIndex == maxIndex && maxIndex != 0) || (realIndex >= minIndex && realIndex < maxIndex))
{
if (startConner == StartConner.UpLeft || startConner == StartConner.LowLeft) //只在增长时才更新grid的尺寸 只加长 不缩短
UpdateRectsize(pos, (temp as RectTransform).pivot);
temp.localPosition = pos;
//设置Item内容
UpdateItem(temp, i, realIndex);
}
}
else if (distance > extents)
{
Vector2 pos = temp.localPosition;
pos.x -= extents * 2f;
int realIndex = getRealIndex(pos);
if ((minIndex == maxIndex && maxIndex != 0) || (realIndex >= minIndex && realIndex < maxIndex))
{
if (startConner == StartConner.UpRight || startConner == StartConner.LowRight)
UpdateRectsize(pos, (temp as RectTransform).pivot);
temp.localPosition = pos;
//设置Item内容
UpdateItem(temp, i, realIndex);
}
}
if (cullContent)//设置裁剪部分是否隐藏
{
Vector2 pos = temp.localPosition;
temp.gameObject.SetActive((pos.x > min && pos.x < max) ? true : false);
}
}
}
else
{
float min = conner_local[0].y - cell_y;//显示区域
float max = conner_local[2].y + cell_y;
for (int i = 0, imax = mChild.Count; i < imax; i++)
{
Transform temp = mChild[i];
float distance = temp.localPosition.y - center.y;
if (distance < -extents)
{
Vector2 pos = temp.localPosition;
pos.y += extents * 2f;
int realIndex = getRealIndex(pos);
if ((minIndex == maxIndex && maxIndex != 0 ) || (realIndex >= minIndex && realIndex < maxIndex))
{
if (startConner == StartConner.LowLeft || startConner == StartConner.LowRight)
UpdateRectsize(pos, (temp as RectTransform).pivot);
temp.localPosition = pos;
//设置Item内容
UpdateItem(temp, i, realIndex);
}
}
else if (distance > extents)
{
Vector2 pos = temp.localPosition;
pos.y -= extents * 2f;
int realIndex = getRealIndex(pos);
if ((minIndex == maxIndex && maxIndex != 0) || (realIndex >= minIndex && realIndex < maxIndex))
{
if (startConner == StartConner.UpLeft || startConner == StartConner.UpRight)
UpdateRectsize(pos, (temp as RectTransform).pivot);
temp.localPosition = pos;
//设置Item内容
UpdateItem(temp, i, realIndex);
}
}
int _realIndex = getRealIndex(temp.localPosition);
if (_realIndex < minIndex || _realIndex >= maxIndex)
{
temp.gameObject.SetActive(false);
}else if (cullContent)//设置裁剪部分是否隐藏
{
temp.gameObject.SetActive((temp.localPosition.y > min && temp.localPosition.y < max) ? true : false);
}
}
}
}
void UpdateItem(Transform item, int index, int realIndex) //跟新Item的内容
{
if (onInitializeItem != null)
{
onInitializeItem(item.gameObject, index, realIndex);
}
}
}