1156 lines
39 KiB
C#
1156 lines
39 KiB
C#
using UnityEngine;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Text;
|
||
using System;
|
||
|
||
namespace Thousandto.Plugins.Common
|
||
{
|
||
/// <summary>
|
||
/// ListView,单列排版的滑动列表,支持不等高度的自动排版
|
||
/// 示例可以参考UIServerListFormScript
|
||
/// </summary>
|
||
public class UIListView : MonoBehaviour
|
||
{
|
||
const float DURATION = 0.5f;
|
||
#region 公共成员
|
||
public UIScrollView ScrollView = null;
|
||
public float LerpDuration = DURATION;
|
||
public bool Initialized = false;
|
||
public float Offset = 50f;
|
||
//默认缩放动画
|
||
public bool EnableEffect = true;
|
||
#endregion
|
||
|
||
#region 私有成员
|
||
private UIPanel _panel = null;
|
||
//最初的偏移
|
||
private Vector4 _originalClipOffset = Vector4.zero;
|
||
//最初的位置
|
||
private Vector3 _originalPos = Vector3.zero;
|
||
//下方最终位置,向上滑动时,如果下方需要补新的item, 坐标从_bottomPos开始算
|
||
private Vector3 _bottomPos = Vector3.zero;
|
||
//上方最终位置,向下滑动时,如果上方需要补新的item,坐标从_topPos开始算
|
||
private Vector2 _topPos = Vector2.zero;
|
||
//需要移动的距离
|
||
//private Vector2 _moveAmount = Vector2.zero;
|
||
//跟节点的上一次变换后的位置
|
||
private Vector3 _lastRootPos = Vector3.zero;
|
||
private Vector2 _lastClipOffset = Vector2.zero;
|
||
//滑动窗口尺寸
|
||
private Vector2 _scrollViewSize = Vector2.zero;
|
||
private Vector2 _autoMoveDistance = Vector2.zero;
|
||
|
||
//添加新的item后,transform需要移动到的目标位置
|
||
private Vector3 _rootMoveToPos = Vector3.zero;
|
||
//添加新的item后,裁剪区域偏移量需要改变到的目标值
|
||
private Vector2 _rootEndClipOffset = Vector2.zero;
|
||
//边界模糊区域
|
||
private Vector2 _softClipOffset = Vector2.zero;
|
||
//ScrollView的边界
|
||
private Bounds _bounds;
|
||
|
||
private Transform _transform = null;
|
||
//在添加了新的item或者滑动区域后需要重新计算边界
|
||
private bool _calclateBounds = false;
|
||
|
||
//添加新item后,自动滑动
|
||
private bool _enableSmoothMove = false;
|
||
//插值时长
|
||
private float _lerpTimer = 0;
|
||
//保存临时Item的节点
|
||
private Transform _cacheRootTrans = null;
|
||
|
||
//--------------下面几个变量用于调用Show方法刷新面板所用
|
||
//是否调用show方法显示列表
|
||
private bool _isCallShowFunc = false;
|
||
private bool _endShow = false;
|
||
//调用show时,记录下沿Y坐标
|
||
private float _bottomClipPosOnShow = 0;
|
||
//显示的索引
|
||
private int _showIndex = 0;
|
||
//------------------------------------------------------
|
||
|
||
#endregion
|
||
|
||
#region 数据
|
||
private List<ItemHolder> _showingList = new List<ItemHolder>();
|
||
private ItemHolder _itemHolder = null;
|
||
private ListItemAdapter _adapter;
|
||
//顶部的item
|
||
private ItemHolder _topItemHolder;
|
||
//底部的item
|
||
private ItemHolder _bottomItemHolder;
|
||
private int _lastIndex = 0;
|
||
//新需要展示消息
|
||
private int _newItemCount = 0;
|
||
|
||
//item间距
|
||
private float _spaceY = 10;
|
||
|
||
private int _curChannel = -1;
|
||
#endregion
|
||
|
||
#region 属性
|
||
//当前窗口的边界
|
||
private Bounds mBounds
|
||
{
|
||
get
|
||
{
|
||
if (!_calclateBounds)
|
||
{
|
||
_calclateBounds = true;
|
||
_bounds = GetBounds(transform);
|
||
}
|
||
|
||
return _bounds;
|
||
}
|
||
}
|
||
|
||
public bool EnableSmoothMove
|
||
{
|
||
get
|
||
{
|
||
return _enableSmoothMove;
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region Mono生命周期
|
||
void Awake()
|
||
{
|
||
_transform = transform;
|
||
ScrollView = GetComponent<UIScrollView>();
|
||
_panel = ScrollView.panel;
|
||
_scrollViewSize = ScrollView.panel.GetViewSize();
|
||
_originalClipOffset = _panel.clipOffset;
|
||
_originalPos = transform.localPosition;
|
||
|
||
if (ScrollView.panel.clipping == UIDrawCall.Clipping.SoftClip)
|
||
{
|
||
_softClipOffset.y = _panel.clipSoftness.y;
|
||
_softClipOffset.x = _panel.clipSoftness.x;
|
||
}
|
||
|
||
InitTopAndBottomPos();
|
||
|
||
//事件注册
|
||
ScrollView.onDragStarted = onDragStarted;
|
||
ScrollView.onDragFinished = onDragFinished;
|
||
ScrollView.onStoppedMoving = onStoppedMoving;
|
||
ScrollView.panel.onClipMove = onDragMove;
|
||
|
||
//自动创建保存Item的节点
|
||
GameObject cacheTempGo = new GameObject("[CacheScrollItem]");
|
||
_cacheRootTrans = cacheTempGo.transform;
|
||
_cacheRootTrans.parent = transform.parent;
|
||
|
||
Clear();
|
||
}
|
||
|
||
//void Update()
|
||
//{
|
||
|
||
//}
|
||
|
||
private void LateUpdate()
|
||
{
|
||
RefreshWhileNewItem();
|
||
|
||
if (_dragging)
|
||
{
|
||
//UnityEngine.Debug.LogError("点击了拖动直接返回不移动滑动列表!!!!!");
|
||
return;
|
||
}
|
||
//onDragMove(null);
|
||
|
||
if (!_enableSmoothMove)
|
||
{
|
||
//UnityEngine.Debug.LogError("滑动列表的SmoothMove状态是关闭的!!!!!");
|
||
return;
|
||
}
|
||
|
||
_lerpTimer += Time.deltaTime / LerpDuration;
|
||
|
||
SmoothMove(_lerpTimer);
|
||
|
||
if (_lerpTimer > 1)
|
||
{
|
||
_lerpTimer = 0;
|
||
_enableSmoothMove = false;
|
||
}
|
||
}
|
||
|
||
#if UNITY_EDITOR
|
||
void OnDrawGizmos()
|
||
{
|
||
Vector4 clip = ScrollView.panel.finalClipRegion;
|
||
|
||
if (Input.mousePosition.y - _lastMousePos.y >= 0)
|
||
_dragMoveDown = false;
|
||
else
|
||
_dragMoveDown = true;
|
||
|
||
Bounds b = GetBounds(transform);
|
||
|
||
float hx = clip.z * 0.5f;
|
||
float hy = clip.w * 0.5f;
|
||
//裁剪区域的上沿Y坐标
|
||
var topClipPos = clip.y + hy;
|
||
//下沿Y坐标
|
||
var bottomClipPos = clip.y - hy;
|
||
|
||
var TopItemOutOfRegion = GetTopItemOutOfClipRegion(topClipPos);
|
||
var TopItemInView = GetTopItemInClipRegion();
|
||
|
||
var BottomItemOutOfRegion = GetBottomItemOutOfClipRegion(bottomClipPos);
|
||
var BottomItemInView = GetBottomItemInClipRegion();
|
||
|
||
Gizmos.color = Color.red;
|
||
if (TopItemOutOfRegion != null)
|
||
{
|
||
Gizmos.DrawLine(ScrollView.transform.TransformPoint(new Vector2(0, TopItemOutOfRegion.TopAndBottom.y)),
|
||
ScrollView.transform.TransformPoint((new Vector2(50 * 5, TopItemOutOfRegion.TopAndBottom.y))));
|
||
UnityEditor.Handles.Label(ScrollView.transform.TransformPoint((new Vector2(50 * 5 + 10, TopItemOutOfRegion.TopAndBottom.y))), "TopItemOutOfRegion-" + TopItemOutOfRegion.Object.name);
|
||
}
|
||
|
||
Gizmos.color = Color.green;
|
||
if (TopItemInView != null)
|
||
{
|
||
Gizmos.DrawLine(ScrollView.transform.TransformPoint(new Vector2(0, TopItemInView.TopAndBottom.x)),
|
||
ScrollView.transform.TransformPoint((new Vector2(50 * 5, TopItemInView.TopAndBottom.x))));
|
||
UnityEditor.Handles.Label(ScrollView.transform.TransformPoint((new Vector2(50 * 5 + 10, TopItemInView.TopAndBottom.x))), "TopItemInView-" + TopItemInView.Object.name);
|
||
}
|
||
|
||
Gizmos.color = Color.blue;
|
||
if (BottomItemInView != null)
|
||
{
|
||
Gizmos.DrawLine(ScrollView.transform.TransformPoint(new Vector2(0, BottomItemInView.TopAndBottom.y)),
|
||
ScrollView.transform.TransformPoint((new Vector2(50 * 5, BottomItemInView.TopAndBottom.y))));
|
||
UnityEditor.Handles.Label(ScrollView.transform.TransformPoint((new Vector2(50 * 5 + 10, BottomItemInView.TopAndBottom.y))), "BottomItemInView-" + BottomItemInView.Object.name);
|
||
}
|
||
|
||
Gizmos.color = Color.black;
|
||
if (BottomItemOutOfRegion != null)
|
||
{
|
||
Gizmos.DrawLine(ScrollView.transform.TransformPoint(new Vector2(0, BottomItemOutOfRegion.TopAndBottom.x)),
|
||
ScrollView.transform.TransformPoint((new Vector2(50 * 5, BottomItemOutOfRegion.TopAndBottom.x))));
|
||
UnityEditor.Handles.Label(ScrollView.transform.TransformPoint((new Vector2(50 * 5 + 10, BottomItemOutOfRegion.TopAndBottom.x))), "BottomItemOutOfRegion-" + BottomItemOutOfRegion.Object.name);
|
||
}
|
||
}
|
||
#endif
|
||
#endregion
|
||
|
||
#region 公共功能函数
|
||
|
||
public List<ItemHolder> GetShowList()
|
||
{
|
||
return _showingList;
|
||
}
|
||
|
||
public void SetAdapter(ListItemAdapter adapter)
|
||
{
|
||
_adapter = adapter;
|
||
Initialized = true;
|
||
}
|
||
|
||
public void SetSpaceY(float space)
|
||
{
|
||
_spaceY = space;
|
||
}
|
||
|
||
public void SetChannel(int channel)
|
||
{
|
||
_curChannel = channel;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示若干item,直到超出可视窗
|
||
/// </summary>
|
||
/// <param name="reverse">采用倒序获取数据</param>
|
||
/// <param name="moveUp"></param>
|
||
public void Show(bool moveDown = false)
|
||
{
|
||
_isCallShowFunc = true;
|
||
_showIndex = 0;
|
||
|
||
var clip = ScrollView.panel.finalClipRegion;
|
||
float hy = clip.w * 0.5f;
|
||
//下沿Y坐标
|
||
_bottomClipPosOnShow = clip.y - hy;
|
||
//_panel.onClipMove = null;
|
||
//int dataCount = _adapter.GetCount();
|
||
//for(int i = 0; i < dataCount; ++i)
|
||
//{
|
||
// var view = _adapter.GetView(i, null);
|
||
// view.Index = i;
|
||
|
||
// if(i == 0)
|
||
// {
|
||
// _topItemHolder = view;
|
||
// }
|
||
|
||
// AddItem(view, false);
|
||
// _showingList.Add(view);
|
||
|
||
// if (_bottomClipPosOnShow - _bottomPos.y > view.Bounds.size.y + Offset)
|
||
// {
|
||
// _lastIndex = i;
|
||
// _bottomItemHolder = view;
|
||
// break;
|
||
// }
|
||
//}
|
||
|
||
if (!moveDown)
|
||
_enableSmoothMove = false;
|
||
}
|
||
|
||
public void ShowReverse(int channel = -1)
|
||
{
|
||
//这里要设置为0,避免切换频道有滑动操作
|
||
ScrollView.currentMomentum = Vector2.zero;
|
||
_dragging = false;
|
||
var clipRegion = ScrollView.panel.finalClipRegion;
|
||
InitTopAndBottomPos(false);
|
||
|
||
if (channel == -1)
|
||
{
|
||
int dataCount = _adapter.GetCount();
|
||
for (int i = dataCount - 1; i >= 0; --i)
|
||
{
|
||
var view = _adapter.GetView(i, null);
|
||
view.Index = i;
|
||
|
||
if (i == dataCount - 1)
|
||
{
|
||
_bottomItemHolder = view;
|
||
}
|
||
|
||
AddItem(view, true);
|
||
_showingList.Insert(0, view);
|
||
|
||
_topItemHolder = view;
|
||
if (_topPos.y - _bottomPos.y > clipRegion.w + Offset)
|
||
{
|
||
_lastIndex = i;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
List<int> indexList = _adapter.GetIndexs(channel);
|
||
for (int i = indexList.Count -1; i >= 0; i--)
|
||
{
|
||
var view = _adapter.GetView(indexList[i], null);
|
||
view.Index = indexList[i];
|
||
|
||
if (i == indexList.Count - 1)
|
||
{
|
||
_bottomItemHolder = view;
|
||
}
|
||
|
||
AddItem(view, true);
|
||
_showingList.Insert(0, view);
|
||
|
||
_topItemHolder = view;
|
||
if (_topPos.y - _bottomPos.y > clipRegion.w + Offset)
|
||
{
|
||
_lastIndex = i;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
SmoothMove(1);
|
||
|
||
//这个脚本导致切换聊天时,如果有滑动操作,新频道内容位置显示异常
|
||
var sp = transform.GetComponent<SpringPanel>();
|
||
if (sp != null)
|
||
{
|
||
sp.target = Vector3.zero;
|
||
sp.enabled = false;
|
||
}
|
||
}
|
||
|
||
private float _addItemDeltaTime = 0;
|
||
//当有新item添加到数据源后触发,需要手动调用
|
||
public bool OnAddItem()
|
||
{
|
||
if (_dragging)
|
||
return false;
|
||
|
||
//上下方如果有超出框的item存在,则不添加新的item
|
||
if ((GetBottomItemOutOfClipRegion() != null/* && GetTopItemOutOfClipRegion() != null*/))
|
||
{
|
||
var topItem = GetTopItemInClipRegion();
|
||
if(topItem != null && topItem.Index != 0)
|
||
return false;
|
||
}
|
||
|
||
if(GetTopItemInClipRegion() == null)
|
||
{
|
||
ScrollView.ResetPosition();
|
||
}
|
||
|
||
//_newItemCount++;
|
||
ShowNewItem();
|
||
|
||
if (_addItemDeltaTime == 0)
|
||
_addItemDeltaTime = Time.realtimeSinceStartup;
|
||
else
|
||
{
|
||
var deltaTime = Time.realtimeSinceStartup - _addItemDeltaTime;
|
||
if (deltaTime < DURATION)
|
||
{
|
||
LerpDuration = deltaTime;
|
||
}
|
||
else
|
||
{
|
||
LerpDuration = DURATION;
|
||
}
|
||
_addItemDeltaTime = Time.realtimeSinceStartup;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
public void ShowNewItem()
|
||
{
|
||
var holder = AddItemFromCacheParent(GetTopItemOutOfClipRegion(), false, GetBottomItemInClipRegion());
|
||
|
||
if (holder == null)
|
||
return;
|
||
|
||
//对新加入的item,播放缩放动画
|
||
PlayScaleEffect(holder);
|
||
}
|
||
|
||
public bool RefreshWhileNewItem()
|
||
{
|
||
//每5帧刷新界面
|
||
//if (Time.frameCount % 5 != 0)
|
||
// return;
|
||
|
||
if(_isCallShowFunc)
|
||
{
|
||
if (Time.frameCount % 5 != 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
if (_endShow)
|
||
{
|
||
_isCallShowFunc = false;
|
||
_endShow = false;
|
||
//_panel.onClipMove = onDragMove;
|
||
return true;
|
||
}
|
||
|
||
var view = _adapter.GetView(_showIndex, null);
|
||
if (view != null)
|
||
{
|
||
view.Index = _showIndex;
|
||
|
||
if (_showIndex == 0)
|
||
{
|
||
_topItemHolder = view;
|
||
}
|
||
|
||
AddItem(view, false);
|
||
_showingList.Add(view);
|
||
|
||
if (_bottomClipPosOnShow - _bottomPos.y > view.Bounds.size.y + Offset)
|
||
{
|
||
_lastIndex = _showIndex;
|
||
_bottomItemHolder = view;
|
||
_showIndex = 0;
|
||
_endShow = true;
|
||
}
|
||
else
|
||
_showIndex++;
|
||
|
||
//对新加入的item,播放缩放动画
|
||
PlayScaleEffect(view);
|
||
}
|
||
else
|
||
_endShow = true;
|
||
}
|
||
else
|
||
{
|
||
if (_newItemCount > 0)
|
||
{
|
||
_newItemCount--;
|
||
ShowNewItem();
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public ItemHolder RemoveAtIndex(int index, bool fromFirst = true, bool forceRemove = false)
|
||
{
|
||
if (_showingList.Count == 0)
|
||
return null;
|
||
|
||
ItemHolder ret = null;
|
||
|
||
if (forceRemove)
|
||
{
|
||
ret = fromFirst ? _showingList[0] : _showingList[_showingList.Count - 1];
|
||
_showingList.RemoveAt(fromFirst ? 0 : _showingList.Count - 1);
|
||
return ret;
|
||
}
|
||
|
||
if (fromFirst)
|
||
{
|
||
//从上到下、从前往后查找
|
||
for(int i = 0; i < _showingList.Count; ++i)
|
||
{
|
||
//找到指定item,删除,其后的item的index需要递减
|
||
if (_showingList[i].Index == index)
|
||
{
|
||
ret = _showingList[i];
|
||
_showingList.RemoveAt(i);
|
||
i--;
|
||
|
||
_topPos.y = ret.TopAndBottom.y;
|
||
}
|
||
else if (_showingList[i].Index > index)
|
||
{
|
||
_showingList[i].Index--;
|
||
}
|
||
}
|
||
|
||
if(ret != null)
|
||
{
|
||
//删除顶部item,需要重新对齐
|
||
if (GetTopItemOutOfClipRegion() == null)
|
||
{
|
||
SetClipRegionMoveOffset(ret.TopAndBottom.y);
|
||
//AddItemFromCacheParent(ret, false, _showingList[_showingList.Count - 1]);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ret = _showingList[_showingList.Count - 1];
|
||
_showingList.RemoveAt(_showingList.Count - 1);
|
||
for(int i = _showingList.Count - 1; i >= 0; --i)
|
||
{
|
||
if(_showingList[i].Index == index)
|
||
{
|
||
ret = _showingList[i];
|
||
_showingList.RemoveAt(i);
|
||
_bottomPos.y = ret.TopAndBottom.x;
|
||
break;
|
||
}
|
||
else
|
||
_showingList[i].Index--;
|
||
|
||
}
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
public void SetAfterItemPos(ItemHolder holder)
|
||
{
|
||
bool isAfter = false;
|
||
List<ItemHolder> tempList = new List<ItemHolder>();
|
||
for (int i = 0; i < _showingList.Count; i++)
|
||
{
|
||
var item = _showingList[i];
|
||
if (!isAfter)
|
||
{
|
||
if (_showingList[i] == holder)
|
||
{
|
||
isAfter = true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
tempList.Add(item);
|
||
}
|
||
}
|
||
float curSizeY = GetBounds(holder.Object).size.y;
|
||
float offsetY = curSizeY - holder.Param;
|
||
holder.Param = curSizeY;
|
||
for (int i = 0; i < tempList.Count; i++)
|
||
{
|
||
var item = tempList[i];
|
||
if (item != null)
|
||
{
|
||
item.Object.localPosition = new Vector3(item.Object.localPosition.x, item.Object.localPosition.y - offsetY, item.Object.localPosition.z);
|
||
var bounds = GetBounds(ScrollView.transform, item.Object);
|
||
item.TopAndBottom.x = bounds.max.y;
|
||
item.TopAndBottom.y = bounds.min.y;
|
||
item.Size = bounds.size;
|
||
}
|
||
}
|
||
//同步bottomPos的坐标
|
||
//_bottomPos.y
|
||
var bottomItem = _showingList[_showingList.Count - 1];
|
||
_bottomPos.y = bottomItem.TopAndBottom.y;
|
||
_bottomPos.y -= _spaceY;
|
||
}
|
||
|
||
public void Clear()
|
||
{
|
||
if (ScrollView == null) return;
|
||
|
||
_showingList.Clear();
|
||
|
||
InitTopAndBottomPos();
|
||
_enableSmoothMove = false;
|
||
|
||
_panel.clipOffset = _originalClipOffset;
|
||
transform.localPosition = _originalPos;
|
||
}
|
||
#endregion
|
||
|
||
#region 回调函数
|
||
|
||
private Vector3 _lastMousePos = Vector3.zero;
|
||
private bool _dragMoveDown = true;
|
||
private bool _dragging = false;
|
||
private void onDragStarted()
|
||
{
|
||
_lastMousePos = Input.mousePosition;
|
||
_dragging = true;
|
||
//手动滑动,就关掉自动滑动
|
||
_enableSmoothMove = false;
|
||
}
|
||
|
||
private void onDragFinished()
|
||
{
|
||
}
|
||
|
||
private void onStoppedMoving()
|
||
{
|
||
_dragging = false;
|
||
}
|
||
|
||
private void onDragMove(UIPanel panel)
|
||
{
|
||
//if (!_dragging)
|
||
// return;
|
||
if (_isCallShowFunc)
|
||
return;
|
||
|
||
var mPanel = ScrollView.panel;
|
||
|
||
bool moveDown = true;
|
||
|
||
Vector4 clip = mPanel.finalClipRegion;
|
||
|
||
if (Input.mousePosition.y - _lastMousePos.y >= 0)
|
||
_dragMoveDown = false;
|
||
else
|
||
_dragMoveDown = true;
|
||
|
||
moveDown = _dragMoveDown;
|
||
|
||
Bounds b = GetBounds(transform);
|
||
|
||
float hx = clip.z * 0.5f;
|
||
float hy = clip.w * 0.5f;
|
||
//裁剪区域的上沿Y坐标
|
||
var topClipPos = clip.y + hy;
|
||
//下沿Y坐标
|
||
var bottomClipPos = clip.y - hy;
|
||
|
||
//上方在裁剪窗口内的item
|
||
var topDisplayItem = GetTopItemInClipRegion();
|
||
//下方在裁剪窗口内的item
|
||
var bottomDisplayItem = GetBottomItemInClipRegion();
|
||
//上方在裁剪窗口外的item
|
||
var topDisappearItem = GetTopItemOutOfClipRegion(topClipPos);
|
||
//下方在裁剪窗口外的item
|
||
var bottomDisappearItem = GetBottomItemOutOfClipRegion(bottomClipPos);
|
||
|
||
if(moveDown)
|
||
{
|
||
//往下滑,需要补充上方的item。如果上方有滑出屏幕的item,则不补充
|
||
if(topDisappearItem == null && topDisplayItem != null)
|
||
{
|
||
AddItemFromCacheParent(bottomDisappearItem, true, topDisplayItem);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//往上滑,需要补充下方的item。如果下方有滑出屏幕的item,则不补充
|
||
if(bottomDisappearItem == null && bottomDisplayItem != null)
|
||
{
|
||
AddItemFromCacheParent(topDisappearItem, false, bottomDisplayItem);
|
||
}
|
||
}
|
||
|
||
|
||
_lastMousePos = Input.mousePosition;
|
||
|
||
}
|
||
#endregion
|
||
|
||
#region 私有功能函数
|
||
|
||
private void InitTopAndBottomPos(bool topLeft = true)
|
||
{
|
||
var clipRegion = ScrollView.panel.finalClipRegion;
|
||
if (topLeft)
|
||
{
|
||
//左上角
|
||
_bottomPos = new Vector2(-clipRegion.z * 0.5f, clipRegion.w * 0.5f);
|
||
}
|
||
else
|
||
{ //左下角
|
||
_bottomPos = new Vector2(-clipRegion.z * 0.5f, -clipRegion.w * 0.5f);
|
||
}
|
||
|
||
_topPos = _bottomPos;
|
||
_topPos.y += _softClipOffset.y + clipRegion.y;
|
||
//clipRegion.y = 0 的情况加上偏移7.7592f
|
||
_bottomPos.y -= _softClipOffset.y + clipRegion.y - 7.7592f;
|
||
}
|
||
|
||
private void AddItem(ItemHolder holder, bool addToTop = true)
|
||
{
|
||
//在这里设置holder非New
|
||
holder.UseNew = false;
|
||
|
||
Transform templateTrans = holder.Object;
|
||
if (templateTrans.parent != ScrollView.transform)
|
||
{
|
||
if(!templateTrans.gameObject.activeSelf)
|
||
templateTrans.gameObject.SetActive(true);
|
||
templateTrans.parent = ScrollView.transform;
|
||
Core.Base.UnityUtils.ResetTransform(templateTrans.gameObject);
|
||
}
|
||
templateTrans.name = "" + holder.Index;
|
||
//var itemTrans = templateTrans;
|
||
Bounds bounds = GetBounds(templateTrans);
|
||
|
||
//设置item的坐标
|
||
SetPosition(templateTrans, bounds, addToTop);
|
||
|
||
bounds = GetBounds(ScrollView.transform, templateTrans);
|
||
holder.TopAndBottom.x = bounds.max.y;
|
||
holder.TopAndBottom.y = bounds.min.y;
|
||
holder.Size = bounds.size;
|
||
|
||
//可重新计算ScrollView的bounds
|
||
_calclateBounds = false;
|
||
|
||
if(!_dragging && !_isCallShowFunc)
|
||
SetMoveAmount();
|
||
|
||
//if (_newItemCount > 5)
|
||
//{
|
||
// SmoothMove(1);
|
||
// RecordLastPosition();
|
||
//}
|
||
//if (!_dragging)
|
||
// SmoothMove(1);
|
||
|
||
TriggeOnItemShow(holder);
|
||
}
|
||
|
||
private Bounds GetBounds(Transform trans)
|
||
{
|
||
return NGUIMath.CalculateRelativeWidgetBounds(trans, trans);
|
||
}
|
||
|
||
private Bounds GetBounds(Transform parent, Transform trans)
|
||
{
|
||
return NGUIMath.CalculateRelativeWidgetBounds(parent, trans);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算添加item后,UIPanel的clipOffset的偏移
|
||
/// </summary>
|
||
/// <param name="toBottom">对齐到底部</param>
|
||
private void SetMoveAmount(bool toBottom = true)
|
||
{
|
||
RecordLastPosition();
|
||
|
||
var mPanel = ScrollView.panel;
|
||
Bounds b = mBounds;
|
||
if (b.min.x == b.max.x || b.min.y == b.max.y) return;
|
||
|
||
Vector4 clip = mPanel.finalClipRegion;
|
||
|
||
float hx = clip.z * 0.5f;
|
||
float hy = clip.w * 0.5f;
|
||
float left = b.min.x + hx;
|
||
float right = b.max.x - hx;
|
||
float bottom = b.min.y + hy;
|
||
float top = b.max.y - hy;
|
||
|
||
if (mPanel.clipping == UIDrawCall.Clipping.SoftClip)
|
||
{
|
||
left -= mPanel.clipSoftness.x;
|
||
right += mPanel.clipSoftness.x;
|
||
bottom -= mPanel.clipSoftness.y;
|
||
top += mPanel.clipSoftness.y;
|
||
}
|
||
|
||
Vector4 cr = mPanel.baseClipRegion;
|
||
float oy = bottom;
|
||
if (b.size.y < cr.w)
|
||
{
|
||
oy = top;
|
||
_lerpTimer = 1;
|
||
}
|
||
|
||
//对齐顶部,主要是用在起始位置的item被删除后,裁剪区域顶部需要和最上面的item对齐
|
||
if (!toBottom)
|
||
oy = top;
|
||
|
||
var pos = transform.localPosition;
|
||
if (ScrollView.canMoveHorizontally)
|
||
{
|
||
_rootMoveToPos.x = pos.x + clip.x - right;
|
||
_rootMoveToPos.y = pos.y;
|
||
_rootEndClipOffset.x = right - cr.x;
|
||
_rootEndClipOffset.y = clip.y - cr.y;
|
||
}
|
||
if (ScrollView.canMoveVertically)
|
||
{
|
||
_rootMoveToPos.x = pos.x;
|
||
_rootMoveToPos.y = pos.y + clip.y - oy;
|
||
_rootEndClipOffset.x = clip.x - cr.x;
|
||
_rootEndClipOffset.y = oy - cr.y;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 手动设置裁剪区域需要移动的偏移
|
||
/// </summary>
|
||
/// <param name="offsetY"></param>
|
||
private void SetClipRegionMoveOffset(float offsetY)
|
||
{
|
||
//RecordLastPosition();
|
||
|
||
var pos = transform.localPosition;
|
||
Vector4 clip = ScrollView.panel.finalClipRegion;
|
||
Vector4 cr = ScrollView.panel.baseClipRegion;
|
||
var topPos = offsetY - cr.w * 0.5f;
|
||
//需要矫正边界坐标,中心点总是跟边界有其一半高度的偏移
|
||
//topPos = topPos - clip.w * 0.5f;
|
||
|
||
Vector3 _rootMoveToPos = pos;
|
||
Vector4 _rootEndClipOffset = clip;
|
||
_rootMoveToPos.x = pos.x;
|
||
_rootMoveToPos.y = pos.y + clip.y - topPos;
|
||
_rootEndClipOffset.x = clip.x;
|
||
_rootEndClipOffset.y = topPos - cr.y;
|
||
|
||
transform.localPosition = _rootMoveToPos;
|
||
ScrollView.panel.clipOffset = _rootEndClipOffset;
|
||
RecordLastPosition();
|
||
}
|
||
|
||
private void SmoothMove(float time)
|
||
{
|
||
_calclateBounds = false;
|
||
if (transform.localPosition == _rootMoveToPos)
|
||
return;
|
||
//transform.localPosition = Vector3.Lerp(_lastRootPos, _rootMoveToPos, time);
|
||
//ScrollView.panel.clipOffset = Vector2.Lerp(_lastClipOffset, _rootEndClipOffset, time);
|
||
transform.localPosition = _rootMoveToPos;
|
||
ScrollView.panel.clipOffset = _rootEndClipOffset;
|
||
//可重新计算ScrollView的bounds
|
||
}
|
||
|
||
private void TriggeOnItemShow(ItemHolder item)
|
||
{
|
||
if (item.OnItemShow != null)
|
||
item.OnItemShow(item);
|
||
}
|
||
|
||
//记录上次变换后的位置
|
||
private void RecordLastPosition()
|
||
{
|
||
_lastRootPos = transform.localPosition;
|
||
_lastClipOffset = ScrollView.panel.clipOffset;
|
||
_lerpTimer = 0;
|
||
|
||
if (!_dragging)
|
||
_enableSmoothMove = true;
|
||
}
|
||
|
||
private void SetPosition(Transform item, Bounds itemBounds, bool addToTop)
|
||
{
|
||
int offsetX = 10;
|
||
var pivot = GetPivot(item);
|
||
if (addToTop)
|
||
{
|
||
var pos = new Vector3(0, _topPos.y - itemBounds.size.y * 0.5f);
|
||
if (pivot == UIWidget.Pivot.TopLeft)
|
||
{
|
||
//pos.x = -itemBounds.size.x * 0.5f;
|
||
pos.x = -_scrollViewSize.x * 0.5f + offsetX;
|
||
pos.y = _topPos.y + itemBounds.size.y;
|
||
}
|
||
else if (pivot == UIWidget.Pivot.Left)
|
||
{
|
||
//pos.x = -itemBounds.size.x ;
|
||
pos.x = -_scrollViewSize.x * 0.5f + offsetX;
|
||
pos.y = _topPos.y + itemBounds.size.y * 0.5f;
|
||
}
|
||
else if (pivot == UIWidget.Pivot.TopRight)
|
||
{
|
||
//pos.x = itemBounds.size.x * 0.5f;
|
||
pos.x = _scrollViewSize.x * 0.5f - offsetX;
|
||
pos.y = _topPos.y + itemBounds.size.y;
|
||
}
|
||
else if (pivot == UIWidget.Pivot.Right)
|
||
{
|
||
//pos.x = itemBounds.size.x * 0.5f;
|
||
pos.x = _scrollViewSize.x * 0.5f - offsetX;
|
||
pos.y = _topPos.y + itemBounds.size.y * 0.5f;
|
||
}
|
||
|
||
//垂直方向,x值是center的值
|
||
item.localPosition = pos;
|
||
//矫正坐标, 需要在设置偏移前调用
|
||
CorrectPosition(item, addToTop);
|
||
|
||
//仅在Y方向做偏移保存
|
||
_topPos.y = _topPos.y + itemBounds.size.y;
|
||
_topPos.y += _spaceY;
|
||
_topPos.x = pos.x;
|
||
}
|
||
else
|
||
{
|
||
var pos = new Vector3(0, _bottomPos.y - itemBounds.size.y * 0.5f);
|
||
if (pivot == UIWidget.Pivot.TopLeft)
|
||
{
|
||
pos.x = -_scrollViewSize.x * 0.5f + offsetX;
|
||
pos.y = _bottomPos.y - itemBounds.size.y;
|
||
}
|
||
else if (pivot == UIWidget.Pivot.Left)
|
||
{
|
||
pos.x = -_scrollViewSize.x + offsetX;
|
||
pos.y = _bottomPos.y - itemBounds.size.y * 0.5f;
|
||
}
|
||
else if (pivot == UIWidget.Pivot.TopRight)
|
||
{
|
||
pos.x = _scrollViewSize.x * 0.5f - offsetX;
|
||
pos.y = _bottomPos.y - itemBounds.size.y;
|
||
}
|
||
else if (pivot == UIWidget.Pivot.Right)
|
||
{
|
||
pos.x = _scrollViewSize.x * 0.5f - offsetX;
|
||
pos.y = _bottomPos.y - itemBounds.size.y * 0.5f;
|
||
}
|
||
|
||
//垂直方向,x值是center的值
|
||
item.localPosition = pos;
|
||
//仅在Y方向做偏移保存
|
||
_bottomPos.y = _bottomPos.y - itemBounds.size.y;
|
||
_bottomPos.y -= _spaceY;
|
||
_bottomPos.x = pos.x;
|
||
|
||
//矫正坐标,需要在设置了偏移后调用
|
||
CorrectPosition(item, addToTop);
|
||
}
|
||
}
|
||
|
||
//矫正位置,因Item设置坐标,其中心点是最上层组件的中心点,而不是整个Transform和子控件构成的大的
|
||
//控件的中心点,导致设置坐标时会有偏移,在这里对偏移做矫正
|
||
private void CorrectPosition(Transform trans, bool addToTop)
|
||
{
|
||
//重新计算设置了坐标后的bounds,主要是得到中心点
|
||
var bounds = NGUIMath.CalculateRelativeWidgetBounds(_transform, trans);
|
||
|
||
if (addToTop)
|
||
trans.localPosition += (new Vector3(0, _topPos.y - bounds.min.y));
|
||
else
|
||
trans.localPosition += (new Vector3(0, _bottomPos.y - bounds.min.y));
|
||
}
|
||
|
||
private UIWidget.Pivot GetPivot(Transform trans)
|
||
{
|
||
var widget = trans.GetComponent<UIWidget>();
|
||
if (widget != null)
|
||
return widget.pivot;
|
||
|
||
return UIWidget.Pivot.Center;
|
||
}
|
||
|
||
private void MoveItemToCacheParent(ItemHolder item, bool disappearTop)
|
||
{
|
||
item.Object.gameObject.SetActive(false);
|
||
|
||
if(disappearTop)
|
||
{
|
||
_topPos.y -= item.Bounds.size.y;
|
||
}
|
||
else
|
||
{
|
||
_bottomPos.y += item.Bounds.size.y;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加新的Item,补充到上或者下方即将呈现的位置
|
||
/// </summary>
|
||
/// <param name="disappearItem">上方或者下方在裁剪区域外的item</param>
|
||
/// <param name="addToTop"></param>
|
||
/// <param name="displayItem">上方或者下方在裁剪区域内的item</param>
|
||
private ItemHolder AddItemFromCacheParent(ItemHolder disappearItem, bool addToTop, ItemHolder displayItem)
|
||
{
|
||
ItemHolder newItem = null;
|
||
if(addToTop)
|
||
{
|
||
var newIndex = displayItem == null ? 0 : _adapter.GetReverseNextIndex(displayItem.Index);
|
||
if (_curChannel != _adapter.GetChannelId(newIndex))
|
||
return null;
|
||
newItem = CreateHolder(newIndex, disappearItem);
|
||
if (newItem == null)
|
||
return newItem;
|
||
|
||
//有裁剪区域外的item才移除末尾的item
|
||
if (disappearItem != null && !newItem.UseNew)
|
||
{
|
||
_showingList.RemoveAt(_showingList.Count - 1);
|
||
if(_showingList.Count > 0)
|
||
{
|
||
var bottomItem = _showingList[_showingList.Count - 1];
|
||
_bottomPos.y = bottomItem.TopAndBottom.y;
|
||
_bottomPos.y -= _spaceY;
|
||
}
|
||
}
|
||
|
||
_showingList.Insert(0, newItem);
|
||
}
|
||
else
|
||
{
|
||
var newIndex = displayItem == null ? 0 : _adapter.GetNextIndex(displayItem.Index);
|
||
if (_curChannel != _adapter.GetChannelId(newIndex))
|
||
return null;
|
||
newItem = CreateHolder(newIndex, disappearItem);
|
||
if (newItem == null)
|
||
return newItem;
|
||
|
||
//有裁剪区域外的item才移除开头的item
|
||
if (disappearItem != null && !newItem.UseNew)
|
||
{
|
||
_showingList.RemoveAt(0);
|
||
if(_showingList.Count > 0)
|
||
{
|
||
_topPos.y = _showingList[0].TopAndBottom.x;
|
||
_topPos.y += _spaceY;
|
||
}
|
||
}
|
||
|
||
_showingList.Add(newItem);
|
||
}
|
||
|
||
AddItem(newItem, addToTop);
|
||
|
||
return newItem;
|
||
}
|
||
|
||
private ItemHolder CreateHolder(int index, ItemHolder temp)
|
||
{
|
||
if (index < 0 || index >= _adapter.GetCount())
|
||
return null;
|
||
|
||
var newItem = _adapter.GetView(index, temp);
|
||
if (newItem == null)
|
||
return null;
|
||
|
||
newItem.Index = index;
|
||
//newItem.Object.parent = _transform;
|
||
return newItem;
|
||
}
|
||
|
||
private void PlayScaleEffect(ItemHolder holder)
|
||
{
|
||
if (holder == null || !EnableEffect)
|
||
return;
|
||
|
||
//if (holder.AnimEffect == null)
|
||
//{
|
||
// holder.AnimEffect = holder.Object.gameObject.RequireComponent<ScaleEffect>();
|
||
// holder.AnimEffect.From = Vector3.one * 0.5f;
|
||
// holder.AnimEffect.To = Vector3.one;
|
||
// holder.AnimEffect.Duration = 0.5f;
|
||
//}
|
||
//holder.Object.localScale = holder.AnimEffect.From;
|
||
//holder.AnimEffect.Start();
|
||
}
|
||
|
||
//上方第一个在裁剪区域内的item
|
||
private ItemHolder GetTopItemInClipRegion()
|
||
{
|
||
if (_showingList.Count > 0)
|
||
return _showingList[0];
|
||
|
||
return null;
|
||
}
|
||
|
||
//下方最后一个在裁剪区域内的item
|
||
private ItemHolder GetBottomItemInClipRegion()
|
||
{
|
||
if (_showingList.Count > 0)
|
||
return _showingList[_showingList.Count - 1];
|
||
|
||
return null;
|
||
}
|
||
|
||
//上方第一个在裁剪区域外的Item
|
||
private ItemHolder GetTopItemOutOfClipRegion(float topClipPos)
|
||
{
|
||
if(_showingList.Count > 0)
|
||
{
|
||
var topItem = _showingList[0];
|
||
if (topItem.TopAndBottom.y > topClipPos + Offset)
|
||
return topItem;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private ItemHolder GetTopItemOutOfClipRegion()
|
||
{
|
||
Vector4 clip = ScrollView.panel.finalClipRegion;
|
||
float hy = clip.w * 0.5f;
|
||
//裁剪区域的上沿Y坐标
|
||
var topClipPos = clip.y + hy;
|
||
|
||
return GetTopItemOutOfClipRegion(topClipPos);
|
||
}
|
||
|
||
//下方最后一个在裁剪区域外的item
|
||
private ItemHolder GetBottomItemOutOfClipRegion(float bottomClipPos)
|
||
{
|
||
if(_showingList.Count > 0)
|
||
{
|
||
var bottomItem = _showingList[_showingList.Count - 1];
|
||
if (bottomItem.TopAndBottom.x < bottomClipPos - Offset)
|
||
return bottomItem;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
//下方最后一个在裁剪区域外的item
|
||
private ItemHolder GetBottomItemOutOfClipRegion()
|
||
{
|
||
Vector4 clip = ScrollView.panel.finalClipRegion;
|
||
float hy = clip.w * 0.5f;
|
||
//下沿Y坐标
|
||
var bottomClipPos = clip.y - hy;
|
||
return GetBottomItemOutOfClipRegion(bottomClipPos);
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|
||
|