Unity ScrollView实现无限滑动效果

本文实例为大家分享了Unity ScrollView实现无限滑动效果的具体代码,供大家参考,具体内容如下

一、效果演示

二、前言

当邮件中有1000封邮件,商店列表中有1000个物体,如果直接实例化1000条数据显示则会大大增加DrawCall,而大量不可见的数据被Mask组件排除在可视范围之外,但他们依然存在,这时就需要考虑通过一个无限滑动的ScrollView来优化渲染性能

三、实现思路

通过头下标和尾下标记录当前实例化数据的最大最小索引,之后用Content的锚点位置与当头下标的锚点位置进行比较判断滑动的方向以及是否超出滑动范围,如果正方向滑动超出范围则将第一个元素移动到最后一个,如果反方向滑动超出范围则将最后一个元素移动到第一个,这样场景中始终存在5个实例化的元素,依次改变元素的位置和显示即可

四、使用说明

——此功能脚本是对ScrollRect的扩展,所以必须添加UGUI提供的基础Scroll View
——Content上必须添加GridLayoutGroup组件,通过GridLayoutGroup组件设计布局,(我在代码中对startCorner、startAxis、childAlignment和constraintCount进行了限制,不需要对其设置)
——不能添加Content Size Fitter组件
——先调用SetTotalCount方法设置总的数据数量再调用Init方法进行初始化
——根据需求修改SetShow方法体
——只适用于单向滑动的情况,不能满足竖直和水平同时滑动的需求,因为大多数无限滑动列表的使用场景都是单向的

五、完整代码

将InfiniteScrollView脚本挂载到ScrollView上

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;

/// <summary>
/// 无限滑动列表
/// </summary>
public class InfiniteScrollView : MonoBehaviour
{
    private ScrollRect scrollRect;//滑动框组件
    private RectTransform content;//滑动框的Content
    private GridLayoutGroup layout;//布局组件

    [Header("滑动类型")]
    public ScrollType scrollType;
    [Header("固定的Item数量")]
    public int fixedCount;
    [Header("Item的预制体")]
    public GameObject itemPrefab;

    private int totalCount;//总的数据数量
    private List<RectTransform> dataList = new List<RectTransform>();//数据实体列表
    private int headIndex;//头下标
    private int tailIndex;//尾下标
    private Vector2 firstItemAnchoredPos;//第一个Item的锚点坐标

    #region Init

    /// <summary>
    /// 实例化Item
    /// </summary>
    private void InitItem()
    {
        for (int i = 0; i < fixedCount; i++)
        {
            GameObject tempItem = Instantiate(itemPrefab, content);
            dataList.Add(tempItem.GetComponent<RectTransform>());
            SetShow(tempItem.GetComponent<RectTransform>(), i);
        }
    }

    /// <summary>
    /// 设置Content大小
    /// </summary>
    private void SetContentSize()
    {
        content.sizeDelta = new Vector2
            (
                layout.padding.left + layout.padding.right + totalCount * (layout.cellSize.x + layout.spacing.x) - layout.spacing.x - content.rect.width,
                layout.padding.top + layout.padding.bottom + totalCount * (layout.cellSize.y + layout.spacing.y) - layout.spacing.y
            );
    }

    /// <summary>
    /// 设置布局
    /// </summary>
    private void SetLayout()
    {
        layout.startCorner = GridLayoutGroup.Corner.UpperLeft;
        layout.startAxis = GridLayoutGroup.Axis.Horizontal;
        layout.childAlignment = TextAnchor.UpperLeft;
        layout.constraintCount = 1;
        if (scrollType == ScrollType.Horizontal)
        {
            scrollRect.horizontal = true;
            scrollRect.vertical = false;
            layout.constraint = GridLayoutGroup.Constraint.FixedRowCount;
        }
        else if (scrollType == ScrollType.Vertical)
        {
            scrollRect.horizontal = false;
            scrollRect.vertical = true;
            layout.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
        }
    }

    /// <summary>
    /// 得到第一个数据的锚点位置
    /// </summary>
    private void GetFirstItemAnchoredPos()
    {
        firstItemAnchoredPos = new Vector2
            (
                layout.padding.left + layout.cellSize.x / 2,
                -layout.padding.top - layout.cellSize.y / 2
            );
    }

    #endregion

    #region Main

    /// <summary>
    /// 滑动中
    /// </summary>
    private void OnScroll(Vector2 v)
    {
        if (dataList.Count == 0)
        {
            Debug.LogWarning("先调用SetTotalCount方法设置数据总数量再调用Init方法进行初始化");
            return;
        }

        if (scrollType == ScrollType.Vertical)
        {
            //向上滑
            while (content.anchoredPosition.y >= layout.padding.top + (headIndex + 1) * (layout.cellSize.y + layout.spacing.y)
            && tailIndex != totalCount - 1)
            {
                //将数据列表中的第一个元素移动到最后一个
                RectTransform item = dataList[0];
                dataList.Remove(item);
                dataList.Add(item);

                //设置位置
                SetPos(item, tailIndex + 1);
                //设置显示
                SetShow(item, tailIndex + 1);

                headIndex++;
                tailIndex++;
            }
            //向下滑
            while (content.anchoredPosition.y <= layout.padding.top + headIndex * (layout.cellSize.y + layout.spacing.y)
                && headIndex != 0)
            {
                //将数据列表中的最后一个元素移动到第一个
                RectTransform item = dataList.Last();
                dataList.Remove(item);
                dataList.Insert(0, item);

                //设置位置
                SetPos(item, headIndex - 1);
                //设置显示
                SetShow(item, headIndex - 1);

                headIndex--;
                tailIndex--;
            }
        }
        else if (scrollType == ScrollType.Horizontal)
        {
            //向左滑
            while (content.anchoredPosition.x <= -layout.padding.left - (headIndex + 1) * (layout.cellSize.x + layout.spacing.x)
            && tailIndex != totalCount - 1)
            {
                //将数据列表中的第一个元素移动到最后一个
                RectTransform item = dataList[0];
                dataList.Remove(item);
                dataList.Add(item);

                //设置位置
                SetPos(item, tailIndex + 1);
                //设置显示
                SetShow(item, tailIndex + 1);

                headIndex++;
                tailIndex++;
            }
            //向右滑
            while (content.anchoredPosition.x >= -layout.padding.left - headIndex * (layout.cellSize.x + layout.spacing.x)
            && headIndex != 0)
            {
                //将数据列表中的最后一个元素移动到第一个
                RectTransform item = dataList.Last();
                dataList.Remove(item);
                dataList.Insert(0, item);

                //设置位置
                SetPos(item, headIndex - 1);
                //设置显示
                SetShow(item, headIndex - 1);

                headIndex--;
                tailIndex--;
            }
        }
    }

    #endregion

    #region Tool

    /// <summary>
    /// 设置位置
    /// </summary>
    private void SetPos(RectTransform trans, int index)
    {
        if (scrollType == ScrollType.Horizontal)
        {
            trans.anchoredPosition = new Vector2
            (
                index == 0 ? layout.padding.left + firstItemAnchoredPos.x :
                layout.padding.left + firstItemAnchoredPos.x + index * (layout.cellSize.x + layout.spacing.x),
                firstItemAnchoredPos.y
            );
        }
        else if (scrollType == ScrollType.Vertical)
        {
            trans.anchoredPosition = new Vector2
            (
                firstItemAnchoredPos.x,
                index == 0 ? -layout.padding.top + firstItemAnchoredPos.y :
                -layout.padding.top + firstItemAnchoredPos.y - index * (layout.cellSize.y + layout.spacing.y)
            );
        }
    }

    #endregion

    #region 外部调用

    /// <summary>
    /// 初始化
    /// </summary>
    public void Init()
    {
        scrollRect = GetComponent<ScrollRect>();
        content = scrollRect.content;
        layout = content.GetComponent<GridLayoutGroup>();
        scrollRect.onValueChanged.AddListener((Vector2 v) => OnScroll(v));

        //设置布局
        SetLayout();

        //设置头下标和尾下标
        headIndex = 0;
        tailIndex = fixedCount - 1;

        //设置Content大小
        SetContentSize();

        //实例化Item
        InitItem();

        //得到第一个Item的锚点位置
        GetFirstItemAnchoredPos();
    }

    /// <summary>
    /// 设置显示
    /// </summary>
    public void SetShow(RectTransform trans, int index)
    {
        //=====根据需求进行编写
        trans.GetComponentInChildren<Text>().text = index.ToString();
        trans.name = index.ToString();
    }

    /// <summary>
    /// 设置总的数据数量
    /// </summary>
    public void SetTotalCount(int count)
    {
        totalCount = count;
    }

    /// <summary>
    /// 销毁所有的元素
    /// </summary>
    public void DestoryAll()
    {
        for (int i = dataList.Count - 1; i >= 0; i--)
        {
            DestroyImmediate(dataList[i].gameObject);
        }
        dataList.Clear();
    }

    #endregion
}

/// <summary>
/// 滑动类型
/// </summary>
public enum ScrollType
{
    Horizontal,//竖直滑动
    Vertical,//水平滑动
}

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

(0)

相关推荐

  • Unity实现滑动更换界面效果

    在做2048这个游戏时,因为菜单页面还能查看游戏规则,而这些规则又不在同一个页上,所以需要滑动页面实现页面切换,但是仅仅使用unity提供的组件做出的效果仅有一个切换的意思,交互感很差,所以在组件的基础上又写了一个控制页面切换的类.而界面切换就是实现一个滚动的视图. 在unity编辑器中实现滚动视图的基本操作:需要用Scroll Rect组件 首先可以看看官方用户手册中关于Scroll Rect组件的讲解,说的很明白.最精辟的描述就是用于使子 RectTransform 滚动的组件. 滚动视图中

  • Unity实现ScrollView滑动吸附功能

    本文实例为大家分享了Unity实现ScrollView滑动吸附的具体代码,供大家参考,具体内容如下 最近在做一个展示模块的时候遇到了一个需要实现滑动窗口并且能固定吸附距离的需求,借助UGUI的ScrollView的API以及Dotween实现了这个功能.主要核心逻辑就是检测Content节点的RectTransform的localPosX的移动距离然后继承实现OnDrag几个接口来完成拖动再松开自动吸附到具体的位置.具体效果如下 另外说一下有几个ScrollView自带的API需要设置一下,一个

  • Unity工具类ScrollView实现拖拽滑动翻页

    简介: 在进行UI设计的时候,经常会使用Unity中UI提供的ScrollView,类似Android中的ScrollView,在进行图片预览,多个翻页的时候,能实现很好的效果. 该类中根据Unity的EventSystems中拖拽事件,实现对页码的滑动监听,在使用的时候,新建UI--->ScrollView,把该类组件添加到ScrollView上,把对应的content加入该脚本中的content,调整ScrollView和Content,设置单个滑动页的宽度,拖拽的阈值,即可监听到拖拽,如果

  • Unity UGUI实现滑动翻页效果

    本文实例为大家分享了Unity UGUI实现滑动翻页效果的具体代码,供大家参考,具体内容如下 这个问题真的是老生常谈的事情了,不过在这里还是要说一下,以便以后之需 首先看一下效果图 最后在Content下面是一些Image using UnityEngine; using System.Collections; using UnityEngine.UI; using System.Collections.Generic; using UnityEngine.EventSystems; using

  • Unity实现颜色渐变滑动条

    本文实例为大家分享了Unity实现颜色渐变滑动条的具体代码,供大家参考,具体内容如下 效果展示 代码 直接挂在UGUI Slider上即可 using UnityEngine; using UnityEngine.UI; public class ColorFade : MonoBehaviour { Color[] colors = new Color[]{ new Color(1, 0, 0), new Color(1, 1, 0), new Color(0, 1, 0), new Colo

  • Unity UGUI实现滑动翻页直接跳转页数

    本文实例为大家分享了Unity UGUI实现滑动翻页,直接跳转页数的具体代码,供大家参考,具体内容如下 首先看一下最终效果 其实这个功能基本上是老生常谈了,所以代码还是很简单 using UnityEngine; using System.Collections; using UnityEngine.UI; using System.Collections.Generic; using UnityEngine.EventSystems; using System; public class Pa

  • Unity ScrollView实现无限滑动效果

    本文实例为大家分享了Unity ScrollView实现无限滑动效果的具体代码,供大家参考,具体内容如下 一.效果演示 二.前言 当邮件中有1000封邮件,商店列表中有1000个物体,如果直接实例化1000条数据显示则会大大增加DrawCall,而大量不可见的数据被Mask组件排除在可视范围之外,但他们依然存在,这时就需要考虑通过一个无限滑动的ScrollView来优化渲染性能 三.实现思路 通过头下标和尾下标记录当前实例化数据的最大最小索引,之后用Content的锚点位置与当头下标的锚点位置进

  • Unity ScrollView实现无限循环效果

    本文实例为大家分享了Unity ScrollView实现无限循环效果的具体代码,供大家参考,具体内容如下 在Unity引擎中ScrollView组件是一个使用率比较高的组件,该组件能上下或者左右拖动的UI列表,背包.展示多个按钮等情况的时候会用到,在做排行榜类似界面时,item非常多,可能有几百个,一次创建这么多GameObject是非常卡的.为此,使用只创建可视区一共显示的个数,加上后置准备个数. 由于ScrollView有两种滚动方式,水平滚动或者垂直滚动,所以我创建了ScrollBase基

  • Unity ScrollView实现自动吸附效果

    本文实例为大家分享了Unity ScrollView实现自动吸附效果的具体代码,供大家参考,具体内容如下 一.效果演示 二.实现思路 通过使用UGUI的拖拽接口,在拖拽结束时比较当前滑动框的NormalizedPositon与每一页的NormalizedPositon值,找到距离当前拖拽结束位置最近的页并缓慢滑动过去 三.使用说明 --此功能脚本是对ScrollView的扩展,所以必须添加UGUI提供的基础Scroll View --Content上必须添加GridLayoutGroup组件并添

  • Unity ScrollRect实现轨迹滑动效果

    本文实例为大家分享了Unity ScrollRect实现轨迹滑动效果的具体代码,供大家参考,具体内容如下 以下内容是根据Unity 2020.1.01f版本进行编写的 1.目的 工作中遇到有需要实现轨迹滑动的滑动列表,通常的做法是计算贝塞尔曲线得出轨迹,但是我觉得计算贝塞尔曲线太麻烦了,或许有没有更简单的方法. 2.思考 轨迹滑动可以分两种情况: 第一种是轨迹滑动是比较简单的横向(或纵向)滑动,其中轨迹不会蜿蜒盘旋,也不涉及列表格子之间的重叠关系,这时可以分区间来对Y轴(或X轴)进行设置以达到格

  • Android使用ViewPager实现无限滑动效果

    前言 其实仔细想一下原理还是挺简单的.无非是当我们滑动到最后一页,再向后滑动时定位到第一页;当我们滑动到第一页,再向前滑动时定位到最后一页. 但是,相信很多朋友都遇到过这个问题:视图的过度效果不自然. 小编也是通过百度和谷歌查找了很多解决方案,实验了很多方法,总结了一个相对不错的方法,接下来给各位分享下滑动效果.实现细节以及一些踩过的坑. 1.无限滑动效果(左右无限滑动) 事先准备好2张滑动图片(有想试验的小伙伴,自备图片啊,小编就不提供了...) 运行效果图(左右无限循环): 为了显示更加直观

  • iOS实现无限滑动效果

    在看到这个标题的时候,相信大家心里肯定会想,无限循环轮播的博客已经满天飞了,好有必要写么.这里我想声明一下,这里的无线滑动,但是数据却不循环. 实现原理 由于业务的需求,需要有大量的数据呈现在collectionView上,但是又不想刷新全部的数据,因此需要制定collectionView的cell的数量为有限的.针对这一种情况,我们需要保证页面刷新数据源的索引和页面滑动的索引是不致的.同时滑动停止后,悄悄的将collectionView恢复到初始的位置.具体源码如下: @interface J

  • android实现切换日期左右无限滑动效果

    大家在进行安卓开发的时候,经常用到日期比如在课程安排,工作安排,日志等地方,今天给大家带来的是让日期左右无限的滑动,并支持自定义显示效果的方法.一起来学习下. 以上是本次所要达到的效果 使用说明: XML布局 新建XML布局 RecyclerView的layout_behavior为com.ldf.calendar.behavior.RecyclerViewBehavior <android.support.design.widget.CoordinatorLayout android:id=&qu

  • UGUI实现ScrollView无限滚动效果

    抽空做了一个UGUI的无限滚动的效果.只做了一半(向下无限滚动).网上也看了很多教程,感觉还是按照自己的思路来写可能比较好.搭建如下: content节点不添加任何组件.布局组件默认是会重新排版子节点的,所以如果子节点的位置变化,会重新排版,不能达到效果.Size Fitter组件也不加,自己写代码调整Size大小(不调整大小,无法滑动). 最主要的实现过程就是用Queue来搬运Cell.在向下滚动的过程中(鼠标上滑),顶部滑出View Port的Cell被搬运到底部续上.这点类似于Queue的

  • IOS实现图片轮播无限循环效果

    本文接着上篇文章进行叙述讲解,主要为大家分享了图片轮播无限循环效果的实现方法,具体内容如下 之前说到第一个问题,ScrollView移动到最后一张图片时无法移动了,这是因为ScrollView已经移动到最后,而图片又是依次排列,自然也就无法移动. 解决办法是,我们换一个思路实现图片轮播效果,ScrollView上只放三个ImageView,屏幕始终显示中间的ImageView,左边和右边的ImageView分别代表前一张图片和后一张图片,屏幕移动的时候,中间的ImageView变化,同时左右两边

  • 使用Swift实现iOScollectionView广告无限滚动效果(DEMO)

    今天公司里的实习生跑过来问我一般App上广告的无限滚动是怎么实现的,刚好很久没写博客了,就决定写下了,尽量帮助那些处于刚学iOS的程序猿. 做一个小demo,大概实现效果如下图所示: 基本实现思路: 1. 在你需要放置无限滚动展示数据的地方把他的数据,在原本的基础上把你要展示的数据扩大三倍.(当然扩大两倍也是可以的,三倍的话,比较好演示) // MARK: - 设置数据源 func collectionView(_ collectionView: UICollectionView, number

随机推荐