热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

UGUI轮播图组件实现方法详解

这篇文章主要为大家详细介绍了UGUI轮播图组件的实现方法,支持自动轮播、手势切换等功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了UGUI轮播图组件实现的具体代码,供大家参考,具体内容如下

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

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

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

下面是鄙人的代码:

/// 主要关注属性、事件及函数:
///  public int CurrentIndex;
///  public Action OnIndexChange;
///  public virtual void MoveToIndex(int ind);
///  public virtual void AddChild(RectTransform t);
/// by yangxun
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;
/// 
/// 轮播图组件
/// 
[RequireComponent(typeof(RectTransform)), ExecuteInEditMode]
public class Carousel : UIBehaviour, IEventSystemHandler, IBeginDragHandler, IInitializePotentialDragHandler, IDragHandler, IEndDragHandler, ICanvasElement {
 
 /// 
 /// 子物体size
 /// 
 public Vector2 CellSize;
 /// 
 /// 子物体间隔
 /// 
 public Vector2 Spacing;
 /// 
 /// 方向
 /// 
 public Axis MoveAxis;
 /// 
 /// Tween时的步数
 /// 
 public int TweenStepCount = 10;
 /// 
 /// 自动轮播
 /// 
 public bool AutoLoop = false;
 /// 
 /// 轮播间隔
 /// 
 public float LoopSpace = 1;
 /// 
 /// 轮播方向--1为向左移动,-1为向右移动
 /// 
 public int LoopDir = 1;
 /// 
 /// 可否拖动
 /// 
 public bool Drag = true;
 /// 
 /// 位于正中的子元素变化的事件,参数为index
 /// 
 public Action OnIndexChange;
 /// 
 /// 当前处于正中的元素
 /// 
 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();
  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 
 /// 加子物体到当前列表的最后面
 /// 
 /// 
 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;
  }
 }
 /// 
 /// 移动到指定索引
 /// 
 /// 
 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;
 }
 /// 
 /// List是否处于可自由调整状态
 /// 
 /// 
 public virtual bool EnsureListCanAdjust() {
  return !m_Dragging && ContentIsLongerThanRect();
 }
 /// 
 /// 内容是否比显示范围大
 /// 
 /// 
 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;
 }
 /// 
 /// 检测边界情况,分为0未触界,-1左(下)触界,1右(上)触界
 /// 
 /// 
 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 = viewRectYMin) {
    return -1;
   }
   else if (r[1].y 
 /// Loop列表,分为-1把最右(上)边一个移到最左(下)边,1把最左(下)边一个移到最右(上)边
 /// 
 /// 
 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;
 }
 /// 
 /// 计算一个最近的正确位置
 /// 
 /// 
 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;
 }
 /// 
 /// 移动指定增量
 /// 
 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只是虚拟的子元素容器大小,这个要注意下。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 处理docker容器时间和宿主机时间不一致问题的方法
    本文介绍了处理docker容器时间和宿主机时间不一致问题的方法,包括复制主机的localtime到容器、处理报错情况以及重启容器的步骤。通过这些方法,可以解决docker容器时间和宿主机时间不一致的问题。 ... [详细]
  • 集合的遍历方式及其局限性
    本文介绍了Java中集合的遍历方式,重点介绍了for-each语句的用法和优势。同时指出了for-each语句无法引用数组或集合的索引的局限性。通过示例代码展示了for-each语句的使用方法,并提供了改写为for语句版本的方法。 ... [详细]
  • Python SQLAlchemy库的使用方法详解
    本文详细介绍了Python中使用SQLAlchemy库的方法。首先对SQLAlchemy进行了简介,包括其定义、适用的数据库类型等。然后讨论了SQLAlchemy提供的两种主要使用模式,即SQL表达式语言和ORM。针对不同的需求,给出了选择哪种模式的建议。最后,介绍了连接数据库的方法,包括创建SQLAlchemy引擎和执行SQL语句的接口。 ... [详细]
  • position属性absolute与relative的区别和用法详解
    本文详细解读了CSS中的position属性absolute和relative的区别和用法。通过解释绝对定位和相对定位的含义,以及配合TOP、RIGHT、BOTTOM、LEFT进行定位的方式,说明了它们的特性和能够实现的效果。同时指出了在网页居中时使用Absolute可能会出错的原因,即以浏览器左上角为原始点进行定位,不会随着分辨率的变化而变化位置。最后总结了一些使用这两个属性的技巧。 ... [详细]
  • 开发笔记:Docker 上安装启动 MySQL
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Docker上安装启动MySQL相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 本文介绍了Java的公式汇总及相关知识,包括定义变量的语法格式、类型转换公式、三元表达式、定义新的实例的格式、引用类型的方法以及数组静态初始化等内容。希望对读者有一定的参考价值。 ... [详细]
  • 本文讨论了微软的STL容器类是否线程安全。根据MSDN的回答,STL容器类包括vector、deque、list、queue、stack、priority_queue、valarray、map、hash_map、multimap、hash_multimap、set、hash_set、multiset、hash_multiset、basic_string和bitset。对于单个对象来说,多个线程同时读取是安全的。但如果一个线程正在写入一个对象,那么所有的读写操作都需要进行同步。 ... [详细]
  • 本文介绍了一种图片处理应用,通过固定容器来实现缩略图的功能。该方法可以实现等比例缩略、扩容填充和裁剪等操作。详细的实现步骤和代码示例在正文中给出。 ... [详细]
  • C++语言入门:数组的基本知识和应用领域
    本文介绍了C++语言的基本知识和应用领域,包括C++语言与Python语言的区别、C++语言的结构化特点、关键字和控制语句的使用、运算符的种类和表达式的灵活性、各种数据类型的运算以及指针概念的引入。同时,还探讨了C++语言在代码效率方面的优势和与汇编语言的比较。对于想要学习C++语言的初学者来说,本文提供了一个简洁而全面的入门指南。 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • 本文介绍了Python函数的定义与调用的方法,以及函数的作用,包括增强代码的可读性和重用性。文章详细解释了函数的定义与调用的语法和规则,以及函数的参数和返回值的用法。同时,还介绍了函数返回值的多种情况和多个值的返回方式。通过学习本文,读者可以更好地理解和使用Python函数,提高代码的可读性和重用性。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
author-avatar
爱这奋斗
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有