UGUI 轮播图组件实现

来源:互联网 发布:2016党规知识网络测试 编辑:程序博客网 时间:2024/05/23 11:51

要用到,于是就自已做了一个,自认为封装上还是OK的,开发于unity5.1.2。

支持自动轮播、手势切换、代码调用切换;支持水平和竖直两个方向以及正负方向轮播,轮播索引改变有回调可以用,也可以获取到当前处于正中的子元素。

要注意的是,向轮播列表中加入新元素不能直接setparent,要调用该组件的AddChild方法

下面是鄙人的代码:

/// 主要关注属性、事件及函数:///     public int CurrentIndex;///     public Action<int> OnIndexChange;///     public virtual void MoveToIndex(int ind);///     public virtual void AddChild(RectTransform t);/// by yangxunusing UnityEngine;using System.Collections;using System.Collections.Generic;using UnityEngine.UI;using UnityEngine.EventSystems;using System;/// <summary>/// 轮播图组件/// </summary>[RequireComponent(typeof(RectTransform)), ExecuteInEditMode]public class Carousel : UIBehaviour, IEventSystemHandler, IBeginDragHandler, IInitializePotentialDragHandler, IDragHandler, IEndDragHandler, ICanvasElement {    /// <summary>    /// 子物体size    /// </summary>    public Vector2 CellSize;    /// <summary>    /// 子物体间隔    /// </summary>    public Vector2 Spacing;    /// <summary>    /// 方向    /// </summary>    public Axis MoveAxis;    /// <summary>    /// Tween时的步数    /// </summary>    public int TweenStepCount = 10;    /// <summary>    /// 自动轮播    /// </summary>    public bool AutoLoop = false;    /// <summary>    /// 轮播间隔    /// </summary>    public float LoopSpace = 1;    /// <summary>    /// 轮播方向--1为向左移动,-1为向右移动    /// </summary>    public int LoopDir = 1;    /// <summary>    /// 可否拖动    /// </summary>    public bool Drag = true;    /// <summary>    /// 位于正中的子元素变化的事件,参数为index    /// </summary>    public Action<int> OnIndexChange;    /// <summary>    /// 当前处于正中的元素    /// </summary>    public int CurrentIndex {        get {            return m_index;        }    }    private bool m_Dragging = false;    private bool m_IsNormalizing = false;    private Vector2 m_CurrentPos;    private int m_currentStep = 0;    private RectTransform viewRectTran;    private Vector2 m_PrePos;    private int m_index = 0,m_preIndex = 0;    private RectTransform header;    private bool contentCheckCache = true;    private float currTimeDelta = 0;    private float viewRectXMin {        get{            Vector3[] v = new Vector3[4];            viewRectTran.GetWorldCorners(v);            return v[0].x;        }    }    private float viewRectXMax {        get {            Vector3[] v = new Vector3[4];            viewRectTran.GetWorldCorners(v);            return v[3].x;        }    }    private float viewRectYMin {        get {            Vector3[] v = new Vector3[4];            viewRectTran.GetWorldCorners(v);            return v[0].y;        }    }    private float viewRectYMax {        get {            Vector3[] v = new Vector3[4];            viewRectTran.GetWorldCorners(v);            return v[2].y;        }    }        public int CellCount {        get {            return transform.childCount;        }    }    protected override void Awake() {        base.Awake();        viewRectTran = GetComponent<RectTransform>();        header = GetChild(viewRectTran, 0);    }    public void resizeChildren() {        //init child size and pos        Vector2 delta;        if (MoveAxis == Axis.Horizontal) {            delta = new Vector2(CellSize.x + Spacing.x, 0);        }        else {            delta = new Vector2(0, CellSize.y + Spacing.y);        }        for (int i = 0; i < CellCount; i++) {            var t = GetChild(viewRectTran, i);            if (t) {                t.localPosition = delta * i;                t.sizeDelta = CellSize;            }        }        m_IsNormalizing = false;        m_CurrentPos = Vector2.zero;        m_currentStep = 0;    }    /// <summary>    /// 加子物体到当前列表的最后面    /// </summary>    /// <param name="t"></param>    public virtual void AddChild(RectTransform t) {        if (t!=null) {            t.SetParent(viewRectTran, false);            t.SetAsLastSibling();            Vector2 delta;            if (MoveAxis == Axis.Horizontal) {                delta = new Vector2(CellSize.x + Spacing.x, 0);            }            else {                delta = new Vector2(0, CellSize.y + Spacing.y);            }            if (CellCount == 0) {                t.localPosition = Vector3.zero;                header = t;            }            else {                t.localPosition = delta + (Vector2)GetChild(viewRectTran,CellCount-1).localPosition;            }        }    }    protected override void OnEnable() {        base.OnEnable();        resizeChildren();        return;        if (Application.isPlaying) {            if (ContentIsLongerThanRect()) {                int s;                do {                    s = GetBoundaryState();                    LoopCell(s);                } while (s != 0);            }        }    }    protected virtual void Update() {        if (ContentIsLongerThanRect()) {            //实现在必要时loop子元素            if (Application.isPlaying) {                int s = GetBoundaryState();                LoopCell(s);            }            //缓动回指定位置            if (m_IsNormalizing && EnsureListCanAdjust()) {                if (m_currentStep == TweenStepCount) {                    m_IsNormalizing = false;                    m_currentStep = 0;                    m_CurrentPos = Vector2.zero;                    return;                }                Vector2 delta = m_CurrentPos/TweenStepCount;                m_currentStep++;                TweenToCorrect(-delta);            }            //自动loop            if (AutoLoop && !m_IsNormalizing && EnsureListCanAdjust()) {                currTimeDelta += Time.deltaTime;                if (currTimeDelta>LoopSpace) {                    currTimeDelta = 0;                    MoveToIndex(m_index + LoopDir);                }            }            //检测index是否变化            if (MoveAxis == Axis.Horizontal) {                m_index = (int)(header.localPosition.x / (CellSize.x + Spacing.x-1));            }            else {                m_index = (int)(header.localPosition.y / (CellSize.y + Spacing.y-1));            }            if (m_index<=0) {                m_index = Mathf.Abs(m_index);            }            else {                m_index = CellCount - m_index;            }            if (m_index != m_preIndex) {                if (OnIndexChange != null) {                    OnIndexChange(m_index);                }            }            m_preIndex = m_index;        }    }    public virtual void OnBeginDrag(PointerEventData eventData) {        if (!Drag || !contentCheckCache) {            return;        }        Vector2 vector;        if (((eventData.button == PointerEventData.InputButton.Left) && this.IsActive()) && RectTransformUtility.ScreenPointToLocalPointInRectangle(this.viewRectTran, eventData.position, eventData.pressEventCamera, out vector)) {            this.m_Dragging = true;            m_PrePos = vector;        }    }    public virtual void OnInitializePotentialDrag(PointerEventData eventData) {        if (!Drag) {            return;        }       return;    }    public virtual void OnDrag(PointerEventData eventData) {        if (!Drag || !contentCheckCache) {            return;        }        Vector2 vector;        if (((eventData.button == PointerEventData.InputButton.Left) && this.IsActive()) && RectTransformUtility.ScreenPointToLocalPointInRectangle(this.viewRectTran, eventData.position, eventData.pressEventCamera, out vector)) {            m_IsNormalizing = false;            m_CurrentPos = Vector2.zero;            m_currentStep = 0;            Vector2 vector2 = vector - this.m_PrePos;            Vector2 vec = CalculateOffset(vector2);            this.SetContentPosition(vec);            m_PrePos = vector;        }    }    /// <summary>    /// 移动到指定索引    /// </summary>    /// <param name="ind"></param>    public virtual void MoveToIndex(int ind) {        if (m_IsNormalizing) {            return;        }        //Debug.LogFormat("{0}->{1}",m_index,ind);        if (ind == m_index) {            return;        }        this.m_IsNormalizing = true;        Vector2 offset;        if (MoveAxis == Axis.Horizontal) {            offset = new Vector2(CellSize.x + Spacing.x, 0);        }        else {            offset = new Vector2(0, CellSize.y + Spacing.y);        }        var delta = CalcCorrectDeltaPos();        int vindex = m_index;        m_CurrentPos = delta + offset * (ind - vindex);        //m_CurrentPos = -(Vector2)header.localPosition + offset * (ind - m_index);        m_currentStep = 0;    }    private Vector2 CalculateOffset(Vector2 delta) {        if (MoveAxis == Axis.Horizontal) {            delta.y = 0;        }        else {            delta.x = 0;        }        return delta;    }    private void SetContentPosition(Vector2 position) {        foreach (RectTransform i in viewRectTran) {            i.localPosition += (Vector3)position;        }        return;    }    public virtual void OnEndDrag(PointerEventData eventData) {        if (!Drag || !contentCheckCache) {            return;        }        this.m_Dragging = false;        this.m_IsNormalizing = true;        m_CurrentPos = CalcCorrectDeltaPos();        m_currentStep = 0;    }    public virtual void Rebuild(CanvasUpdate executing) {        return;    }    /// <summary>    /// List是否处于可自由调整状态    /// </summary>    /// <returns></returns>    public virtual bool EnsureListCanAdjust() {        return !m_Dragging && ContentIsLongerThanRect();    }    /// <summary>    /// 内容是否比显示范围大    /// </summary>    /// <returns></returns>    public virtual bool ContentIsLongerThanRect() {        float contentLen;        float rectLen;        if (MoveAxis == Axis.Horizontal) {            contentLen = CellCount*(CellSize.x + Spacing.x) - Spacing.x;            rectLen = viewRectTran.rect.xMax - viewRectTran.rect.xMin;        }        else {            contentLen = CellCount * (CellSize.y + Spacing.y) - Spacing.y;            rectLen = viewRectTran.rect.yMax - viewRectTran.rect.yMin;        }        contentCheckCache = contentLen > rectLen;        return contentCheckCache;    }    /// <summary>    /// 检测边界情况,分为0未触界,-1左(下)触界,1右(上)触界    /// </summary>    /// <returns></returns>    public virtual int GetBoundaryState() {        RectTransform left;        RectTransform right;        left = GetChild(viewRectTran, 0);        right = GetChild(viewRectTran, CellCount - 1);        Vector3[] l = new Vector3[4];        left.GetWorldCorners(l);        Vector3[] r = new Vector3[4];        right.GetWorldCorners(r);        if (MoveAxis == Axis.Horizontal) {            if (l[0].x>=viewRectXMin) {                return -1;            }            else if (r[3].x < viewRectXMax) {                return 1;            }        }        else {            if (l[0].y >= viewRectYMin) {                return -1;            }            else if (r[1].y < viewRectYMax) {                return 1;            }        }        return 0;    }    /// <summary>    /// Loop列表,分为-1把最右(上)边一个移到最左(下)边,1把最左(下)边一个移到最右(上)边    /// </summary>    /// <param name="dir"></param>    protected virtual void LoopCell(int dir) {        if (dir == 0) {            return;        }        RectTransform MoveCell;        RectTransform Tarborder;        Vector2 TarPos;        if (dir == 1) {            MoveCell = GetChild(viewRectTran, 0);            Tarborder = GetChild(viewRectTran, CellCount - 1);            MoveCell.SetSiblingIndex(CellCount-1);        }        else {            Tarborder = GetChild(viewRectTran, 0);            MoveCell = GetChild(viewRectTran, CellCount - 1);            MoveCell.SetSiblingIndex(0);        }        if (MoveAxis == Axis.Horizontal) {            TarPos = Tarborder.localPosition + new Vector3((CellSize.x + Spacing.x) * dir, 0,0);        }        else {            TarPos = (Vector2)Tarborder.localPosition + new Vector2(0, (CellSize.y + Spacing.y) * dir);        }        MoveCell.localPosition = TarPos;    }    /// <summary>    /// 计算一个最近的正确位置    /// </summary>    /// <returns></returns>    public virtual Vector2 CalcCorrectDeltaPos() {        Vector2 delta = Vector2.zero;        float distance = float.MaxValue;        foreach (RectTransform i in viewRectTran) {            var td = Mathf.Abs(i.localPosition.x) + Mathf.Abs(i.localPosition.y);            if (td<=distance) {                distance = td;                delta = i.localPosition;            }            else {                break;            }        }        return delta;    }    /// <summary>    /// 移动指定增量    /// </summary>    protected virtual void TweenToCorrect(Vector2 delta) {        foreach (RectTransform i in viewRectTran) {            i.localPosition += (Vector3)delta;        }    }    public enum Axis {        Horizontal,        Vertical    }    private static RectTransform GetChild(RectTransform parent, int index) {        if (parent == null||index>=parent.childCount) {            return null;        }        return parent.GetChild(index) as RectTransform;    }}


用法和ugui的scrollrect组件是差不多的,因为本来在drag事件上有所借鉴

例图如下:

另外,它不会像ugui的几个布局组件一样自动去改变子元素的大小为cellsize,cellsize只是虚拟的子元素容器大小,这个要注意下。

0 0
原创粉丝点击