UGUI ScrollRect滑动居中CenterOnChild实现

NGUI有一个UICenterOnChild脚本,可以轻松实现ScrollView中拖动子物体后保持一个子物体位于中心位置。然而UGUI就没这么方便了,官方并没有类似功能的脚本。网上找到一些运行效果都不对,可能因为UGUI需要配置的东西太多,RectTransfrom不同设置效果就不一样。故自己实现了该功能,使用时的配置如下:

1. 仅适用于水平方向拖动的ScrollRect。
2. ScrollRect中的Grid必须使用GridLayoutGroup。
3. 由于需要知道ScrollRect的宽度以便计算中心位置,故ScrollRect的Anchors的四个小三角中的上面或者下面的一对角不得分离,不然宽度计算出错,即需要:Anchors.Min.x == Anchors.Max.x。最好四角合一。
4. 由于是通过设置ScrollRect's content的localPosition实现,故需要将ScrollRect的中心点Pivot与content的中心点均置于自身最左边(0, 0.5)。
5. 由于第一个与最后一个子物体需要停留在中间,故ScrollRect的Movement Type需要设置为Unrestricted。该项会在运行时自动设置。

代码如下:

  1     /// <summary>
  2     /// 
  3     /// 拖动ScrollRect结束时始终让一个子物体位于中心位置。
  4     /// 
  5     /// </summary>
  6     public class CenterOnChild : MonoBehaviour, IEndDragHandler, IDragHandler
  7     {
  8         //将子物体拉到中心位置时的速度
  9         public float centerSpeed = 9f;
 10 
 11         //注册该事件获取当拖动结束时位于中心位置的子物体
 12         public delegate void OnCenterHandler (GameObject centerChild);
 13         public event OnCenterHandler onCenter;
 14 
 15         private ScrollRect _scrollView;
 16         private Transform _container;
 17 
 18         private List<float> _childrenPos = new List<float> ();
 19         private float _targetPos;
 20         private bool _centering = false;
 21 
 22         void Awake ()
 23         {
 24             _scrollView = GetComponent<ScrollRect> ();
 25             if (_scrollView == null)
 26             {
 27                 Debug.LogError ("CenterOnChild: No ScrollRect");
 28                 return;
 29             }
 30             _container = _scrollView.content;
 31 
 32             GridLayoutGroup grid;
 33             grid = _container.GetComponent<GridLayoutGroup> ();
 34             if (grid == null)
 35             {
 36                 Debug.LogError ("CenterOnChild: No GridLayoutGroup on the ScrollRect's content");
 37                 return;
 38             }
 39 
 40             _scrollView.movementType = ScrollRect.MovementType.Unrestricted;
 41 
 42             //计算第一个子物体位于中心时的位置
 43             float childPosX = _scrollView.GetComponent<RectTransform> ().rect.width * 0.5f - grid.cellSize.x * 0.5f;
 44             _childrenPos.Add (childPosX);
 45             //缓存所有子物体位于中心时的位置
 46             for (int i = 0; i < _container.childCount - 1; i++)
 47             {
 48                 childPosX -= grid.cellSize.x + grid.spacing.x;
 49                 _childrenPos.Add (childPosX);
 50             }
 51         }
 52 
 53         void Update ()
 54         {
 55             if (_centering)
 56             {
 57                 Vector3 v = _container.localPosition;
 58                 v.x = Mathf.Lerp (_container.localPosition.x, _targetPos, centerSpeed * Time.deltaTime);
 59                 _container.localPosition = v;
 60                 if (Mathf.Abs (_container.localPosition.x - _targetPos) < 0.01f)
 61                 {
 62                     _centering = false;
 63                 }
 64             }
 65         }
 66 
 67         public void OnEndDrag (PointerEventData eventData)
 68         {
 69             _centering = true;
 70             _targetPos = FindClosestPos (_container.localPosition.x);
 71         }
 72 
 73         public void OnDrag (PointerEventData eventData)
 74         {
 75             _centering = false;
 76         }
 77 
 78         private float FindClosestPos (float currentPos)
 79         {
 80             int childIndex = 0;
 81             float closest = 0;
 82             float distance = Mathf.Infinity;
 83 
 84             for (int i = 0; i < _childrenPos.Count; i++)
 85             {
 86                 float p = _childrenPos[i];
 87                 float d = Mathf.Abs (p - currentPos);
 88                 if (d < distance)
 89                 {
 90                     distance = d;
 91                     closest = p;
 92                     childIndex = i;
 93                 }
 94             }
 95 
 96             GameObject centerChild = _container.GetChild (childIndex).gameObject;
 97             if (onCenter != null)
 98                 onCenter (centerChild);
 99 
100             return closest;
101         }
102     }

由于已缓存所有子物体的位置信息,故该代码简单修改可以扩展功能,如增加到上一项、到下一项、跳转到指定项等功能。

posted @ 2016-05-27 17:21  桫椤  阅读(14281)  评论(0编辑  收藏  举报