Unity之图片轮播组件实现

来源:互联网 发布:科密x1考勤机导出数据 编辑:程序博客网 时间:2024/05/17 06:00

游戏中有时候会见到图片轮播的效果,那么这里就自己封装了一个,包括自动轮播、切页按钮控制、页码下标更新、滑动轮播、切页后的回调等等
下面,先上一个简陋的gif动态效果图

这里写图片描述

从图中可以看出,该示例包括了三张图片的轮播,左右分别是上一张和下一张的按钮,右下角显示了当前是第几章的页码下标。

直接上脚本:

using System;using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.Events;using UnityEngine.EventSystems;using UnityEngine.UI;namespace UnityEngine.UI{    [AddComponentMenu("UI/Slidershow", 39)]          //添加菜单    [ExecuteInEditMode]                             //编辑模式下可执行    [DisallowMultipleComponent]                     //不可重复    [RequireComponent(typeof(RectTransform))]       //依赖于RectTransform组件    public class Slideshow : UIBehaviour,IPointerDownHandler,IPointerUpHandler    {        public enum MovementType        {            /// <summary>            /// 循环            /// </summary>            Circulation,        //循环,轮播到最后一页之后,直接回到第一页            /// <summary>            /// 来回往复            /// </summary>            PingPong,           //来回往复,轮播到最后一页之后,倒序轮播,到第一页之后,同理        }        public enum MoveDir        {            Left,            Right,        }        [SerializeField]        private MovementType m_movement = MovementType.Circulation;        public MovementType Movement { get { return m_movement; } set { m_movement = value; } }        [SerializeField]        private RectTransform m_content;        public RectTransform Content { get { return m_content; } set { m_content = value; } }        [SerializeField]        private Button m_lastPageButton;        public Button LastPageButton { get { return m_lastPageButton; } set { m_lastPageButton = value; } }        [SerializeField]        private Button m_nextPageButton;        public Button NextPageButton { get { return m_nextPageButton; } set { m_nextPageButton = value; } }        /// <summary>        /// 自动轮播时长        /// </summary>        [SerializeField]        private float m_showTime = 2.0f;        public float ShowTime { get { return m_showTime; } set { m_showTime = value; } }        /// <summary>        /// 是否自动轮播        /// </summary>        [SerializeField]        private bool m_autoSlide = false;        public bool AutoSlide { get { return m_autoSlide; }set { m_autoSlide = value; } }        /// <summary>        /// 自动轮播方向,-1表示向左,1表示向右        /// </summary>        private MoveDir m_autoSlideDir = MoveDir.Right;        /// <summary>        /// 是否允许拖动切页        /// </summary>        [SerializeField]        private bool m_allowDrag = true;        public bool AllowDrag { get { return m_allowDrag; }set { m_allowDrag = value; } }        /// <summary>        /// 当前显示页的页码,下标从0开始        /// </summary>        private int m_curPageIndex = 0;        public int CurPageIndex { get { return m_curPageIndex; } }        /// <summary>        /// 最大页码        /// </summary>        private int m_maxPageIndex = 0;        public int MaxPageIndex { get { return m_maxPageIndex; } }        /// <summary>        /// 圆圈页码ToggleGroup        /// </summary>        [SerializeField]        private ToggleGroup m_pageToggleGroup;        public ToggleGroup PageToggleGroup { get { return m_pageToggleGroup; } set { m_pageToggleGroup = value; } }        /// <summary>        /// 圆圈页码Toggle List        /// </summary>        private List<Toggle> m_pageToggleList;        public List<Toggle> PageToggleLise { get { return m_pageToggleList; }}        //item数目        private int m_itemNum = 0;        public int ItemNum { get { return m_itemNum; } }        //以Toggle为Key,返回页码        private Dictionary<Toggle, int> m_togglePageNumDic = null;        private float m_time = 0f;        private List<float> m_childItemPos = new List<float>();        private GridLayoutGroup m_grid = null;        protected override void Awake()        {            base.Awake();            if (null == m_content)            {                throw new Exception("Slideshow content is null");            }            else            {                m_grid = m_content.GetComponent<GridLayoutGroup>();                if (m_grid == null)                {                    throw new Exception("Slideshow content is miss GridLayoutGroup Component");                }                InitChildItemPos();            }            if (null != m_lastPageButton)            {                m_lastPageButton.onClick.AddListener(OnLastPageButtonClick);            }            if (null != m_nextPageButton)            {                m_nextPageButton.onClick.AddListener(OnNextPageButtonClick);            }            if (null != m_pageToggleGroup)            {                int toggleNum = m_pageToggleGroup.transform.childCount;                if (toggleNum > 0)                {                    m_pageToggleList = new List<Toggle>();                    m_togglePageNumDic = new Dictionary<Toggle, int>();                    for (int i = 0; i < toggleNum; i++)                    {                        Toggle childToggle = m_pageToggleGroup.transform.GetChild(i).GetComponent<Toggle>();                        if (null != childToggle)                        {                            m_pageToggleList.Add(childToggle);                            m_togglePageNumDic.Add(childToggle, i);                            childToggle.onValueChanged.AddListener(OnPageToggleValueChanged);                        }                    }                    m_itemNum = m_pageToggleList.Count;                    m_maxPageIndex = m_pageToggleList.Count - 1;                }            }            UpdateCutPageButtonActive(m_curPageIndex);        }        private void InitChildItemPos()        {            int childCount = m_content.transform.childCount;            float cellSizeX = m_grid.cellSize.x;            float spacingX = m_grid.spacing.x;            float posX = -cellSizeX * 0.5f;            m_childItemPos.Add(posX);            for (int i = 1; i < childCount; i++)            {                posX -= cellSizeX + spacingX;                m_childItemPos.Add(posX);            }        }        private void OnPageToggleValueChanged(bool ison)        {            if (ison)            {                Toggle activeToggle = GetActivePageToggle();                if (m_togglePageNumDic.ContainsKey(activeToggle))                {                    int page = m_togglePageNumDic[activeToggle];                    SwitchToPageNum(page);                }            }        }        private Toggle GetActivePageToggle()        {            if (m_pageToggleGroup == null || m_pageToggleList == null || m_pageToggleList.Count <= 0)            {                return null;            }            for (int i = 0; i < m_pageToggleList.Count; i++)            {                if (m_pageToggleList[i].isOn)                {                    return m_pageToggleList[i];                }            }            return null;        }        /// <summary>        /// 切换至某页        /// </summary>        /// <param name="pageNum">页码</param>        private void SwitchToPageNum(int pageNum)        {            if (pageNum < 0 || pageNum > m_maxPageIndex)            {                throw new Exception("page num is error");            }            if (pageNum == m_curPageIndex)            {                //目标页与当前页是同一页                return;            }            m_curPageIndex = pageNum;            if (m_movement == MovementType.PingPong)            {                UpdateCutPageButtonActive(m_curPageIndex);            }            Vector3 pos = m_content.localPosition;            m_content.localPosition = new Vector3(m_childItemPos[m_curPageIndex], pos.y, pos.z);            m_pageToggleList[m_curPageIndex].isOn = true;            if (m_onValueChanged != null)            {                //执行回调                m_onValueChanged.Invoke(m_pageToggleList[m_curPageIndex].gameObject);            }        }        /// <summary>        /// 根据页码更新切页按钮active        /// </summary>        /// <param name="pageNum"></param>        private void UpdateCutPageButtonActive(int pageNum)        {            if (pageNum == 0)            {                UpdateLastButtonActive(false);                UpdateNextButtonActive(true);            }            else if (pageNum == m_maxPageIndex)            {                UpdateLastButtonActive(true);                UpdateNextButtonActive(false);            }            else            {                UpdateLastButtonActive(true);                UpdateNextButtonActive(true);            }        }        private void OnNextPageButtonClick()        {            m_time = Time.time;     //重新计时            switch (m_movement)            {                case MovementType.Circulation:                    SwitchToPageNum((m_curPageIndex + 1) % m_itemNum);                    break;                case MovementType.PingPong:                    //该模式下,会自动隐藏切页按钮                    SwitchToPageNum(m_curPageIndex + 1);                    break;                default:                    break;            }            Debug.Log(m_content.localPosition);        }        private void OnLastPageButtonClick()        {            m_time = Time.time; //重新计时            switch (m_movement)            {                case MovementType.Circulation:                    SwitchToPageNum((m_curPageIndex + m_itemNum - 1) % m_itemNum);                    break;                case MovementType.PingPong:                    //该模式下,会自动隐藏切页按钮                    SwitchToPageNum(m_curPageIndex - 1);                    break;                default:                    break;            }        }        private void UpdateLastButtonActive(bool activeSelf)        {            if (null == m_lastPageButton)            {                throw new Exception("Last Page Button is null");            }            bool curActive = m_lastPageButton.gameObject.activeSelf;            if (curActive != activeSelf)            {                m_lastPageButton.gameObject.SetActive(activeSelf);            }        }        private void UpdateNextButtonActive(bool activeSelf)        {            if (null == m_nextPageButton)            {                throw new Exception("Next Page Button is null");            }            bool curActive = m_nextPageButton.gameObject.activeSelf;            if (curActive != activeSelf)            {                m_nextPageButton.gameObject.SetActive(activeSelf);            }        }        private Vector3 m_originDragPos = Vector3.zero;        private Vector3 m_desDragPos = Vector3.zero;        private bool m_isDrag = false;        public void OnPointerDown(PointerEventData eventData)        {            if (!m_allowDrag)            {                return;            }            if (eventData.button != PointerEventData.InputButton.Left)            {                return;            }            if (!IsActive())            {                return;            }            m_isDrag = true;            m_originDragPos = eventData.position;        }        public void OnPointerUp(PointerEventData eventData)        {            m_desDragPos = eventData.position;            MoveDir dir = MoveDir.Right;            if (m_desDragPos.x < m_originDragPos.x)            {                dir = MoveDir.Left;            }            switch (dir)            {                case MoveDir.Left:                    if (m_movement == MovementType.Circulation || (m_movement == MovementType.PingPong && m_curPageIndex != 0))                    {                        OnLastPageButtonClick();                    }                    break;                case MoveDir.Right:                    if (m_movement == MovementType.Circulation || (m_movement == MovementType.PingPong && m_curPageIndex != m_maxPageIndex))                    {                        OnNextPageButtonClick();                    }                    break;            }            m_isDrag = false;        }        /// <summary>        /// 切页后回调函数        /// </summary>        [Serializable]        public class SlideshowEvent : UnityEvent<GameObject> { }        [SerializeField]        private SlideshowEvent m_onValueChanged = new SlideshowEvent();        public SlideshowEvent OnValueChanged { get { return m_onValueChanged; } set { m_onValueChanged = value; } }        public override bool IsActive()        {            return base.IsActive() && m_content != null;        }        private void Update()        {            if (m_autoSlide && !m_isDrag)            {                if (Time.time > m_time + m_showTime)                {                    m_time = Time.time;                    switch (m_movement)                    {                        case MovementType.Circulation:                            m_autoSlideDir = MoveDir.Right;                            break;                        case MovementType.PingPong:                            if (m_curPageIndex == 0)                            {                                m_autoSlideDir = MoveDir.Right;                            }                            else if (m_curPageIndex == m_maxPageIndex)                            {                                m_autoSlideDir = MoveDir.Left;                            }                            break;                    }                    switch (m_autoSlideDir)                    {                        case MoveDir.Left:                            OnLastPageButtonClick();                            break;                        case MoveDir.Right:                            OnNextPageButtonClick();                            break;                    }                }            }        }    }}

这里提供了一个枚举MovementType,该枚举定义了两种循环方式,其中Circulation循环,是指轮播到最后一页之后,直接回到第一页;而PingPong相信大家你熟悉了,就是来回往复的。

其中还提供了对每张图显示的时长进行设置,还有是否允许自动轮播的控制,是否允许拖动切页控制,等等。。其实将图片作为轮播子元素只是其中之一而已,完全可以将ScrollRect作为轮播子元素,这样每个子元素又可以滑动阅览了。。

这里还提供了两个编辑器脚本,一个是SlideshowEditor(依赖Slideshow组件),另一个是给用户提供菜单用的CreateSlideshow,代码分别如下:

using System.Collections;using System.Collections.Generic;using UnityEditor;using UnityEngine;using UnityEngine.EventSystems;using UnityEngine.UI;public class CreateSlideshow : Editor{    private static GameObject m_slideshowPrefab = null;    private static GameObject m_canvas = null;    [MenuItem("GameObject/UI/Slideshow")]    static void CreateSlideshowUI(MenuCommand menuCommand)    {        if (null == m_slideshowPrefab)        {            m_slideshowPrefab = Resources.Load<GameObject>("Slideshow");            if (null == m_slideshowPrefab)            {                Debug.LogError("Prefab Slideshow is null");                return;            }        }        m_canvas = menuCommand.context as GameObject;        if (m_canvas == null || m_canvas.GetComponentInParent<Canvas>() == null)        {            m_canvas = GetOrCreateCanvasGameObject();        }        GameObject go = GameObject.Instantiate(m_slideshowPrefab, m_canvas.transform);        go.transform.localPosition = Vector3.zero;        go.name = "Slideshow";        Selection.activeGameObject = go;    }    static public GameObject GetOrCreateCanvasGameObject()    {        GameObject selectedGo = Selection.activeGameObject;        Canvas canvas = (selectedGo != null) ? selectedGo.GetComponentInParent<Canvas>() : null;        if (canvas != null && canvas.gameObject.activeInHierarchy)            return canvas.gameObject;        canvas = Object.FindObjectOfType(typeof(Canvas)) as Canvas;        if (canvas != null && canvas.gameObject.activeInHierarchy)            return canvas.gameObject;        return CreateCanvas();    }    public static GameObject CreateCanvas()    {        var root = new GameObject("Canvas");        root.layer = LayerMask.NameToLayer("UI");        Canvas canvas = root.AddComponent<Canvas>();        canvas.renderMode = RenderMode.ScreenSpaceOverlay;        root.AddComponent<CanvasScaler>();        root.AddComponent<GraphicRaycaster>();        Undo.RegisterCreatedObjectUndo(root, "Create " + root.name);        CreateEventSystem();        return root;    }    public static void CreateEventSystem()    {        var esys = Object.FindObjectOfType<EventSystem>();        if (esys == null)        {            var eventSystem = new GameObject("EventSystem");            GameObjectUtility.SetParentAndAlign(eventSystem, null);            esys = eventSystem.AddComponent<EventSystem>();            eventSystem.AddComponent<StandaloneInputModule>();            Undo.RegisterCreatedObjectUndo(eventSystem, "Create " + eventSystem.name);        }    }}
using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEditor.Advertisements;using UnityEngine.UI;namespace UnityEditor.UI{    [CustomEditor(typeof(Slideshow), true)]    public class SlideshowEditor : Editor    {        SerializedProperty m_movement;        SerializedProperty m_content;        SerializedProperty m_lastPageButton;        SerializedProperty m_nextPageButton;        SerializedProperty m_showTime;        SerializedProperty m_pageToggleGroup;        SerializedProperty m_onValueChanged;        SerializedProperty m_allowDrag;        SerializedProperty m_autoSlide;        protected virtual void OnEnable()        {            m_movement = serializedObject.FindProperty("m_movement");            m_content = serializedObject.FindProperty("m_content");            m_lastPageButton = serializedObject.FindProperty("m_lastPageButton");            m_nextPageButton = serializedObject.FindProperty("m_nextPageButton");            m_showTime = serializedObject.FindProperty("m_showTime");            m_pageToggleGroup = serializedObject.FindProperty("m_pageToggleGroup");            m_onValueChanged = serializedObject.FindProperty("m_onValueChanged");            m_allowDrag = serializedObject.FindProperty("m_allowDrag");            m_autoSlide = serializedObject.FindProperty("m_autoSlide");        }        public override void OnInspectorGUI()        {            serializedObject.Update();            EditorGUILayout.PropertyField(m_movement);            EditorGUILayout.PropertyField(m_content);            EditorGUILayout.PropertyField(m_lastPageButton);            EditorGUILayout.PropertyField(m_nextPageButton);            EditorGUILayout.PropertyField(m_allowDrag);            EditorGUILayout.PropertyField(m_autoSlide);            EditorGUILayout.PropertyField(m_showTime);            EditorGUILayout.PropertyField(m_pageToggleGroup);            EditorGUILayout.Space();            EditorGUILayout.PropertyField(m_onValueChanged);            //不加这句代码,在编辑模式下,无法将物体拖拽赋值            serializedObject.ApplyModifiedProperties();        }    }}

这两个脚本中使用了一些拓展编辑器的知识,后续在另外写博客介绍
其中脚本CreateSlideshow中使用UGUI源码中的DefaultControls脚本里的方法,有兴趣可以去下载查阅。
Demo工程下载地址如下:
链接:http://pan.baidu.com/s/1i4Rralf 密码:7loe
链接如有私有,请及时联系补充

以上知识分享,如有错误,欢迎指出,共同学习,共同进步

原创粉丝点击