IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity 支持水平,竖直。无限轮播 ,基于UGUI的ScrollView组件 -> 正文阅读

[游戏开发]Unity 支持水平,竖直。无限轮播 ,基于UGUI的ScrollView组件

Unity 支持水平,竖直。无限轮播 ,基于UGUI的ScrollView组件

1,实图
在这里插入图片描述
2,代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 支持 水平 和 垂直无限拖动
/// </summary>
/// <typeparam name="T"></typeparam>
public class BaseList<T>
{
    public delegate Transform SetElement(Transform transform, T itemData);
    /// <summary>
    /// //显示列数
    /// </summary>
    public int 列数 = 1;
    /// <summary>
    /// 行距
    /// </summary>
    public float 行间距;
    /// <summary>
    /// 列距
    /// </summary>
    public float 列间距;
    /// <summary>
    /// // 可显示行数
    /// </summary>
    private int 行数 = 1;
    private Transform content;         //List的基本组件
    private float parentWidth = 0;      //List的宽度
    private float parentHeight = 0;      //List的高度
    private Transform itemTransform;    //显示的ITem
    private float itemWidth = 0;        //子物体宽度
    private float itemHeight = 0;       //子物体高度
    private int 显示数量 = 0;          //显示的子物体数量
    private float 可视范围 = 0;      //可视范围
    private float 内容总长度 = 0;    //总长度
    private bool is水平滑动;
    private float contentStartPos = 0;         //开始位置

    // private float maxY;                 //最大可显示高度
    // private float minY;                 //最小可显示高度

    private Transform rootTransform = null;

    //private List<string> dataList = null;

    private List<T> dataList;
    private SetElement setElement;

    private Stack<Transform> itemStack; //搞个栈存储Item,方便存取

    private Dictionary<int, Transform> itemDictionary = new Dictionary<int, Transform>();


    /// <summary>
    /// 第一个参数是设置的列表数据
    /// 第二个参数是设置显示方式的方法
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="list"></param>
    /// <param name="setElement"></param> 
    public void SetParam(List<T> list, SetElement setElement, Transform root, float rowDistance, float columnDistance, bool isH)
    {
        is水平滑动 = isH;
        行间距 = rowDistance;
        列间距 = columnDistance;
        rootTransform = root;
        dataList = list;
        this.setElement = setElement;
        InitParameter();
    }

    /// <summary>
    /// 参数初始化
    /// </summary>
    private void InitParameter()
    {
        //可视范围
        content = rootTransform.Find("Viewport/Content");

        //要实例化的item
        itemTransform = rootTransform.Find("Viewport/Item").transform;
        itemTransform.gameObject.SetActive(false);
        Rect itemRect = itemTransform.GetComponent<RectTransform>().rect;
        itemWidth = itemRect.width;
        itemHeight = itemRect.height;
        //可视范围 - 父物体
        parentWidth = rootTransform.GetComponent<RectTransform>().rect.width;
        parentHeight = rootTransform.GetComponent<RectTransform>().rect.height;

        if (!is水平滑动)  // 垂直
        {
            contentStartPos = content.localPosition.y;
            可视范围 = rootTransform.GetComponent<RectTransform>().rect.height;
            //int maxcolumnCount = (int)(parentWidth / itemWidth);
            // if (maxcolumnCount < 列数)
            //列数 = maxcolumnCount;

            行数 = (int)((parentHeight - (行间距 + itemHeight / 2)) / (行间距 + itemHeight) + 1);

            // maxY = itemHeight / 2;
            // minY = (行间距 + itemHeight) * 行数;
        }
        else // 水平滑动
        {
            contentStartPos = content.localPosition.x;
            可视范围 = rootTransform.GetComponent<RectTransform>().rect.width;

            列数 = (int)((parentWidth - (列间距 + itemWidth / 2)) / (列间距 + itemWidth) + 1);

        }
        显示数量 = 列数 * 行数;
        Debug.Log("列数:" + 列数 + "行数:" + 行数 + "显示数量:" + 显示数量);
        SetData();
    }

    /// <summary>
    /// 设置元素
    /// </summary>
    /// <param name="list"></param>
    public void SetData()
    {
        int maxCount = dataList.Count;
        int index = 0;
        if (!is水平滑动)
        {
            内容总长度 = (itemHeight + 行间距) * maxCount / 列数;
            //重新计算Content的宽高用来装Item,但是这个x轴的宽度很神奇填0是ok的但是不规范坐等大神解决(自行调试发现问题我暂时没有解决)
            content.GetComponent<RectTransform>().sizeDelta = new Vector2(content.GetComponent<RectTransform>().sizeDelta.x, 内容总长度 + 10);
        }
        else
        {
            内容总长度 = (itemWidth + 列间距) * maxCount / 行数;
            //重新计算Content的宽高用来装Item,但是这个x轴的宽度很神奇填0是ok的但是不规范坐等大神解决(自行调试发现问题我暂时没有解决)
            content.GetComponent<RectTransform>().sizeDelta = new Vector2(内容总长度 + 10, content.GetComponent<RectTransform>().sizeDelta.y);
        }
        Debug.Log("内容总长度:" + 内容总长度);

        ScrollRect scrollRect = rootTransform.GetComponent<ScrollRect>();
        if (scrollRect != null)
        {
            scrollRect.onValueChanged.RemoveAllListeners();
            scrollRect.onValueChanged.AddListener(OnValueChange);   //监听滑动值的改变,传入的是位置信息 
        }
        if (!is水平滑动)
        {
            for (int row = 0; row < 行数 + 1; row++)
            {
                for (int column = 0; column < 列数; column++)
                {
                    if (index >= maxCount)
                        return;
                    Transform itemTrans = setElement(GameObject.Instantiate(itemTransform.gameObject, content.transform).transform, dataList[index]);
                    itemTrans.gameObject.SetActive(true);
                    //itemTrans.GetComponent<RectTransform>().anchorMin = new Vector2(0, 1);
                    //itemTrans.GetComponent<RectTransform>().anchorMax = new Vector2(0, 1);
                    // float x = (column * (itemWidth + 列间距) + itemWidth / 2 + 列间距);
                    //float x = parentWidth / 2f;
                    //float x = 0;
                    //前面物体的距离=行数*(物体高度+物体间离)
                    //自己再往后退半个+间距
                    float y = -(row * (itemHeight + 行间距) + itemHeight / 2 + 行间距);

                    itemTrans.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, y); //设置位置

                    //itemTrans.localPosition = new Vector2(0, y);   
                    // Debug.Log("设置位置:" + itemTrans.localPosition);
                    itemDictionary.Add(index, itemTrans);
                    index++;
                }
                if (index >= maxCount)
                    return;
            }
        }
        else
        {
            for (int col = 0; col < 列数 + 1; col++)
            {
                for (int row = 0; row < 行数; row++)
                {
                    if (index >= maxCount)
                        return;

                    Transform itemTrans = setElement(GameObject.Instantiate(itemTransform.gameObject, content.transform).transform, dataList[index]);

                    itemTrans.gameObject.SetActive(true);
                    //itemTrans.GetComponent<RectTransform>().anchorMin = new Vector2(0, 1);
                    //itemTrans.GetComponent<RectTransform>().anchorMax = new Vector2(0, 1);
                    float x = (col * (itemWidth + 列间距) + itemWidth / 2 + 列间距);
                    //前面物体的距离=行数*(物体高度+物体间离)
                    //自己再往后退半个+间距
                    // float y = -(row * (itemHeight + 行间距) + itemHeight / 2 + 行间距);
                    //float y = parentHeight / 2f;
                    itemTrans.GetComponent<RectTransform>().anchoredPosition = new Vector2(x, 0);   //设置位置            
                    itemDictionary.Add(index, itemTrans);
                    index++;
                }
                if (index >= maxCount)
                    return;

            }
        }
    }


    /// <summary>
    /// 监听List滑动
    /// 记录一下思路,我们需要的是显示在可视范围内的物体
    /// 这个移动监听事件的值是0-1,最上面是1,最下面是0
    /// 我们现在有最大长度,可视长度,可视长度/最大长度 = 可视比例 比如长度是10,可视距离是1,我们的可视比例是1/10
    /// 那么如果我们把页面拉到0.2的时候,只要子物体在0.2*最大长度和0.2*(1/10)*最大长度的范围内,我们就让它进行显示,超出这个范围的就存到栈内
    /// </summary>
    /// <param name="vector"></param>
    public void OnValueChange(Vector2 vector)
    {
        //获取当前显示的最后一个
        int showIndex = GetShowIndex();
        //不能小于第一个,不能大到剩下的item不足以容下完整一页
        if (showIndex < 1) showIndex = 1;
        //else if (startIndex - (dataList.Count - showCount) < 0) startIndex = (dataList.Count - showCount);
        //可视比例
        // float overallProp = 可视范围 / 内容总长度;
        // float maxY = content.transform.InverseTransformPoint(new Vector3(0, vector.y * overallLength, 0)).y;   //最大高度
        // float minY = content.transform.InverseTransformPoint(new Vector3(0, vector.y * overallProp, 0)).y;     //最小高度

        //int index = showIndex - 1; //虽然找到了开始位置,但是数组下标是从0开始的

        //int endIndex = startIndex + (dataList.Count - 显示数量);

        List<int> uplist = new List<int>();
        List<int> downList = new List<int>();
        //清空不在范围内的数据存到队列中
        foreach (int key in itemDictionary.Keys)
        {
            if (!is水平滑动)
            {
                //当前物体在可视范围之上
                if (key < showIndex - 1 && key + ((列数 * (行数 + 1))) < dataList.Count)
                    uplist.Add(key);
                //当前物体在可视范围之下
                if (key > showIndex + (列数 * (行数 + 1)) - 列数 - 1)
                    downList.Add(key);
            }
            else
            {
                //当前物体在可视范围之左
                if (key < showIndex - 1 && key + (行数 * (列数 + 1)) < dataList.Count)
                    uplist.Add(key);
                //当前物体在可视范围之右
                if (key > showIndex + (行数 * (列数 + 1)) - 行数 - 1)
                    downList.Add(key);
            }
        }
        //删除上面的表示物体往下滑了,
        //我们要填充的是该位置往下拉可视范围数量
        foreach (int cursor in uplist)
        {
            Transform trans;
            if (itemDictionary.TryGetValue(cursor, out trans))
            {
                itemDictionary.Remove(cursor);
                if (!is水平滑动)
                {
                    int row = cursor / 列数 + (列数 * (行数 + 1)) / 列数;  //拉到第几行                  
                    int indexPos = cursor + (列数 * (行数 + 1));
                    float colum = -(row * (itemHeight + 行间距) + itemHeight / 2 + 行间距);   //计算出该行位置
                    if ((列数 * (行数 + 1)) + cursor < dataList.Count)
                    {
                        trans = setElement(trans, dataList[(列数 * (行数 + 1)) + cursor]);
                        trans.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, colum);
                        //trans.localPosition = new Vector2(trans.localPosition.x, colum);
                        itemDictionary.Add(indexPos, trans);
                    }
                }
                else
                {
                    int col = cursor / 行数 + (行数 * (列数 + 1)) / 行数;  //拉到第几列
                    int indexPos = cursor + (行数 * (列数 + 1));
                    float colum = (col * (itemWidth + 列间距) + itemWidth / 2 + 列间距);   //计算出该列位置
                    if ((行数 * (列数 + 1)) + cursor < dataList.Count)
                    {
                        trans = setElement(trans, dataList[(行数 * (列数 + 1)) + cursor]);
                        trans.GetComponent<RectTransform>().anchoredPosition = new Vector2(colum, 0);
                        //trans.localPosition = new Vector2(colum, trans.localPosition.y);
                        itemDictionary.Add(indexPos, trans);
                    }
                }
            }
        }

        //删除上面的表示物体往下滑了,
        //我们要填充的是该位置往下拉可视范围数量
        foreach (int cursor in downList)
        {
            Transform trans;
            if (itemDictionary.TryGetValue(cursor, out trans))
            {
                itemDictionary.Remove(cursor);
                if (!is水平滑动)
                {
                    int row = cursor / 列数 - (列数 * (行数 + 1)) / 列数;  //拉到第几行
                    int indexPos = cursor - (列数 * (行数 + 1));
                    float rowPos = -(row * (itemHeight + 行间距) + itemHeight / 2 + 行间距);  //计算出该行位置
                    trans = setElement(trans, dataList[cursor - (列数 * (行数 + 1))]);
                    trans.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, rowPos);
                    //trans.localPosition = new Vector2(trans.localPosition.x, rowPos);
                    itemDictionary.Add(indexPos, trans);
                }
                else
                {
                    int col = cursor / 行数 - (行数 * (列数 + 1)) / 行数;  //拉到第几列
                    int indexPos = cursor - (行数 * (列数 + 1));
                    float colPos = (col * (itemWidth + 列间距) + itemWidth / 2 + 列间距);  //计算出该列位置
                    trans = setElement(trans, dataList[cursor - (行数 * (列数 + 1))]);
                    trans.GetComponent<RectTransform>().anchoredPosition = new Vector2(colPos, 0);
                    //trans.localPosition = new Vector2(colPos, trans.localPosition.y);
                    itemDictionary.Add(indexPos, trans);
                }

            }
        }

    }

    /// <summary>
    /// 获取到要从第几个位置开始显示
    /// 这里的做法就是最开始的位置减去当前位置
    /// 往下滑值越低,往上滑值越高。
    /// (初始位置-当前位置)/(item垂直距离+item高度)+1 = 从第几行开始显示
    /// 行数*3+1 
    /// </summary>
    public int GetShowIndex()
    {
        float startPos = contentStartPos;
        float currentPos;
        int line = 0;
        int startIndex = -1;
        if (!is水平滑动)
        {
            currentPos = content.localPosition.y;
            line = ((int)((currentPos - startPos) / (itemHeight + 行间距)) + 1);
            startIndex = line * 列数 - 列数 + 1;
        }
        else
        {
            currentPos = content.localPosition.x;
            line = ((int)(Mathf.Abs(currentPos - startPos) / (itemWidth + 列间距)) + 1);
            startIndex = line * 行数 - 行数 + 1;
        }
        return startIndex;
    }


}



3,使用时调用

 BaseList<T> baseList = new BaseList<T>();
        baseList.SetParam(list, setElement, this.transform, rowDistance, columnDistance, is水平滑动);

4,下方是使用例子欢迎下载
示例下载地址

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-08-14 14:25:30  更:2021-08-14 14:28:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 18:32:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码