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     }

由於已緩存所有子物體的位置信息,故該代碼簡單修改可以擴展功能,如增加到上一項、到下一項、跳轉到指定項等功能。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM