unity ScrollView循环拖动Item


前言

在网上找到的大部分要么单行单列的,要么是不能配合Scrollbar使用的,就在其他人的基础上改了一下


提示:以下是本篇文章正文内容,下面案例可供参考

一、注意事项

1.ScrollView里面的Content上的锚点要改一下.
在这里插入图片描述
2.GridLayoutGroup里面的StartConer,StartAxixs,ChildAlignment,Constrint会跟代码改变,
3.要用到界面上要有GridLayoutGroup组件
4.ScrollRect里面的Horzontal和Vertical控制是上下拖动还是左右拖动
在这里插入图片描述

3.其他的按ScrollView的正常使用方式设置就行了.

二、使用步骤

1.Item代码

拖动的子物体代码,要继承一个GridPartItem脚本,里面有三个虚方法,作为通用方法,被继承

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GridPartItem 
{
	public GameObject ga;
	public virtual void UpdateItem(object itemData)
	{
	}
	public virtual void OnBindItemElements(GameObject ga)
	{
		this.ga = ga;
	}
	public virtual void Dispose()
	{ 
	}
}

写一个Item类,用来接受动态生成的gameobject,以及一些交互的代码


using UnityEngine;
using UnityEngine.UI;
public class Item : GridPartItem
{
	public Text text;
    public override void OnBindItemElements(GameObject ga)
    {
        base.OnBindItemElements(ga);
        text = ga.transform.Find("Text").GetComponent<Text>();
    }
    public override void UpdateItem(object itemData)
	{
        //text= 
        ga.name = (string)itemData;

        text.text = (string)itemData;
	}
}

2.调用

1.在panel上面挂载一个脚本demo,
其中InfiniteScrollView脚本是对ScrollView里面的生成Gameobject进行重新定位,和循环使用的脚本.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class demo : MonoBehaviour
{
    public GameObject scrollView;
    public GameObject itemPrefab;
    // Start is called before the first frame update
    void Start()
    {
        //添加一个数据
        List<string> strList = new List<string>() {"name","周亚伟","小灰灰","大灰灰","胖灰灰","瘦灰灰","haha", "周亚伟1" , "小灰灰1" , "大灰灰1" , "胖灰灰1" , "瘦灰灰1" , "haha1" , "周亚伟2" , "小灰灰2" , "大灰灰2" , "胖灰灰2" , "瘦灰灰2" , "haha2" };

        //实例化一个InfiniteScrollView
        //其中Item类,可以替换为其他item脚本
        InfiniteScrollView<Item> item = new InfiniteScrollView<Item>(scrollView , itemPrefab);
       
        List<object> datas = new List<object>();
        strList.ForEach(a => datas.Add(a));
        //设置item上的数据,数据是一个List,采用通用数据Object格式
        item.SetObjectList(datas);
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

3.InfiniteScrollView脚本

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

/// <summary>
/// 无限滑动列表
/// </summary>
public class InfiniteScrollView<T> where T : GridPartItem,new ()
{
    /// <summary>
    /// 滑动框组件
    /// </summary>
    private ScrollRect scrollRect;
    /// <summary>
    /// 滑动框的Content
    /// </summary>
    private RectTransform content;
    /// <summary>
    /// 布局组件
    /// </summary>
    private GridLayoutGroup layout;//布局组件

    /// <summary>
    /// 滑动类型
    /// </summary>
    [Header("滑动类型")]
    public ScrollType scrollType;
    /// <summary>
    /// 生成初始化生成item数量
    /// </summary>
    private int fixedCount;
    [Header("Item的预制体")]
    public GameObject itemPrefab;
    /// <summary>
    /// 总的item数据数量
    /// </summary>
    private int totalCount;
    /// <summary>
    /// 数据实体类 RectTransform
    /// </summary>
    private List<RectTransform> dataList = new List<RectTransform>();
    private List<T> itemScriptsList = new List<T>();
    private List<object> objectList = new List<object>();
    /// <summary>
    /// 头下标
    /// </summary>
    private int headIndex;
    /// <summary>
    /// 尾下标
    /// </summary>
    private int tailIndex;
    /// <summary>
    /// 第一个Item的锚点坐标
    /// </summary>
    private Vector2 firstItemAnchoredPos;
    /// <summary>
    /// 获取mask初始宽高
    /// </summary>
    private Vector2 maskTranV2;
    /// <summary>
    /// 在mask初始宽高下,一行能放item最多数量,一列放item最多数量
    /// </summary>
    private int maxItemNumWide, maxItemNumHigh;
    #region Init

    /// <summary>
    /// 实例化Item
    /// </summary>
    private void InitItem()
    {
        int num = fixedCount <= totalCount ? fixedCount : totalCount;
        for (int i = 0; i < num; i++)
        {
            GameObject tempItem =GameObject.Instantiate(itemPrefab , content);
            dataList.Add(tempItem.GetComponent<RectTransform>());
            T itemScript = new T();
            itemScript.OnBindItemElements(tempItem);
            itemScriptsList.Add(itemScript);
            SetShow(itemScript,i);
        }
    }

    /// <summary>
    /// 设置Content大小
    /// </summary>
    private void SetContentSize()
    {
        if (scrollType == ScrollType.Horizontal)
        {
            content.sizeDelta = new Vector2
             (
                 layout.padding.left + layout.padding.right + (int)Math.Ceiling((float)totalCount / maxItemNumHigh) * (layoutCellSizeXAndSpacingX) -layout.spacing.x ,
                 content.rect.height
             ); ;
        }
        else if (scrollType == ScrollType.Vertical)
        {
            content.sizeDelta = new Vector2
               (
                   content.rect.width ,
                   layout.padding.top + layout.padding.bottom + (int)Math.Ceiling((float)totalCount / maxItemNumWide) * layoutCellSizeYAndSpacingY
               ); ;
        }
    }

    /// <summary>
    /// 设置布局
    /// </summary>
    private void SetLayout()
    {
        layout.startCorner = GridLayoutGroup.Corner.UpperLeft;
        layout.childAlignment = TextAnchor.UpperLeft;
        layout.constraint = Constraint.Flexible;
        if (scrollType == ScrollType.Horizontal)
        {
            layout.startAxis = GridLayoutGroup.Axis.Vertical;
            scrollRect.horizontal = true;
            scrollRect.vertical = false;
            fixedCount = (maxItemNumWide+3) * maxItemNumHigh;
        }
        else if (scrollType == ScrollType.Vertical)
        {
            layout.startAxis = GridLayoutGroup.Axis.Horizontal;
            scrollRect.horizontal = false;
            scrollRect.vertical = true;
            fixedCount = maxItemNumWide * (maxItemNumHigh+3);
        }
    }

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

    #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 / maxItemNumWide + 1) * layoutCellSizeYAndSpacingY)
            && tailIndex != totalCount - 1)
            {
                //将数据列表中的第一个元素移动到最后一个
                RectTransform item = dataList[0];
                dataList.Remove(item);
                dataList.Add(item);
                //设置位置
                SetPos(item , tailIndex + 1);

                T itemScript = itemScriptsList[0];
                itemScriptsList.Remove(itemScript);
                itemScriptsList.Add(itemScript);

                SetShow(itemScript , tailIndex + 1);
                headIndex++;
                tailIndex++;
            }
            //向下滑
            while (content.anchoredPosition.y <= (layout.padding.top + (headIndex / maxItemNumWide + 1) * layoutCellSizeYAndSpacingY)
                && headIndex != 0)
            {

                //将数据列表中的最后一个元素移动到第一个
                RectTransform item = dataList.Last();
                dataList.Remove(item);
                dataList.Insert(0 , item);
                
                //设置位置
                SetPos(item , headIndex - 1);

                T itemScript = itemScriptsList.Last();
                itemScriptsList.Remove(itemScript);
                itemScriptsList.Insert(0 , itemScript);
                SetShow(itemScript , headIndex - 1);

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

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

                T itemScript = itemScriptsList[0];
                itemScriptsList.Remove(itemScript);
                itemScriptsList.Add( itemScript);
                SetShow(itemScript , tailIndex + 1);

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

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

                T itemScript = itemScriptsList.Last();
                itemScriptsList.Remove(itemScript);
                itemScriptsList.Insert(0 , itemScript);
                SetShow(itemScript , headIndex - 1);

                headIndex--;
                tailIndex--;
            }
        }
    }

    #endregion

    #region Tool
    /// <summary>
    /// 设置位置
    /// </summary>
    private void SetPos(RectTransform trans , int index)
    {
        if (scrollType == ScrollType.Horizontal)
        {
            int rowNum = (int)Math.Ceiling((double)index / maxItemNumHigh);
            rowNum = index % maxItemNumHigh == 0 ? rowNum + 1 : rowNum;
            //列
            int lineNum = index % maxItemNumHigh + 1;
            trans.anchoredPosition = new Vector2(
                rowNum == 1 ? firstItemAnchoredPos.x : (rowNum - 1) * layoutCellSizeXAndSpacingX + layout.padding.left + layout.cellSize.x / 2 ,
                 lineNum == 1 ? firstItemAnchoredPos.y : -((lineNum - 1) * layoutCellSizeYAndSpacingY + layout.padding.top + layout.cellSize.y / 2)
                );
        }
        else if (scrollType == ScrollType.Vertical)
        {

            int rowNum = (int)Math.Ceiling((double)index / maxItemNumWide);
            rowNum = index % maxItemNumWide == 0 ? rowNum + 1 : rowNum;
            //列
            int lineNum = index % maxItemNumWide + 1;
            //trans.anchoredPosition = new Vector2(
                //lineNum == 1 ? firstItemAnchoredPos.x : (lineNum - 1) * (layout.cellSize.x + layout.spacing.x) + layout.padding.left + layout.cellSize.x / 2 ,
                // rowNum == 1 ? firstItemAnchoredPos.y : -((rowNum - 1) * (layout.cellSize.y + layout.spacing.y) + layout.padding.top + layout.cellSize.y / 2)
                //);
            trans.anchoredPosition = new Vector2(
                lineNum == 1 ? firstItemAnchoredPos.x : (lineNum - 1) * layoutCellSizeXAndSpacingX + layout.padding.left + layout.cellSize.x / 2 ,
                 rowNum == 1 ? firstItemAnchoredPos.y : -((rowNum - 1) * layoutCellSizeYAndSpacingY + layout.padding.top + layout.cellSize.y / 2)
                ); 
        }
    }
    #endregion

    #region 外部调用

    public InfiniteScrollView(GameObject ga,GameObject itemPrefab)
    {
        this.itemPrefab = itemPrefab;
        Init(ga);
    }
    private int layoutCellSizeYAndSpacingY, layoutCellSizeXAndSpacingX;
    /// <summary>
    /// 初始化
    /// </summary>
    public void Init(GameObject ga)
    {
        //显示item的界面宽高
        maskTranV2 = ga.GetComponent<RectTransform>().sizeDelta;
        scrollRect = ga.GetComponent<ScrollRect>();
        content = scrollRect.content;
        layout = content.GetComponent<GridLayoutGroup>();
        scrollRect.onValueChanged.AddListener((Vector2 v) => OnScroll(v));
        layoutCellSizeYAndSpacingY = (int)layout.cellSize.y + (int)layout.spacing.y;
        layoutCellSizeXAndSpacingX = (int)layout.cellSize.x + (int)layout.spacing.x;
        maxItemNumWide = (int)((maskTranV2.x - layout.padding.left - layout.padding.top) / layoutCellSizeXAndSpacingX);
        maxItemNumHigh = (int)((maskTranV2.y - layout.padding.right - layout.padding.bottom) / layoutCellSizeYAndSpacingY);

        scrollType = scrollRect.horizontal ? ScrollType.Horizontal : ScrollType.Vertical;
        //设置布局
        SetLayout();
    }
    public void SetObjectList(List<object> objectList)
    {
        this.objectList = objectList;
        //设置总的item数量
        totalCount = objectList.Count;
        fixedCount = totalCount <= fixedCount ? totalCount : fixedCount;
        //设置Content大小
        SetContentSize();
        //设置头下标和尾下标
        headIndex = 0;
        tailIndex = fixedCount - 1;
        //实例化Item
        InitItem();

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

    /// <summary>
    /// 设置显示
    /// </summary>
    public void SetShow(T itemScript,int index)
    {
        itemScript.UpdateItem(objectList[index]);
    }

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

    #endregion
}

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

总结

里面有些代码是从其他博客里找的,忘了是哪个了
演示demo
没了

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现无限循环ScrollView,可以使用 Unity 的 UI 布局组件和代码结合的方式来实现。 首先,在 ScrollView 中添加一个 Content 布局组件,用于控制 ScrollView 中的子控件排列。 然后,在代码中动态生成需要显示的子控件,并将其添加到 Content 中。为了实现无限循环,需要在首尾各添加一个相同的子控件,这样在滑动到末尾时,可以无缝地切换到开头,实现循环。 以下是一个简单的示例代码,可以放在 ScrollView 的父物体上: ```csharp using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class InfiniteScrollView : MonoBehaviour { public GameObject itemPrefab; public int itemCount = 10; private List<GameObject> itemList = new List<GameObject>(); private RectTransform contentRectTransform; private float itemHeight; private int currentItemIndex = 0; void Start() { // 获取 Content 的 RectTransform 组件 contentRectTransform = GetComponentInChildren<ScrollRect>().content; // 获取子控件的高度 itemHeight = itemPrefab.GetComponent<RectTransform>().rect.height; // 生成子控件 for (int i = 0; i < itemCount + 2; i++) { GameObject item = Instantiate(itemPrefab, contentRectTransform); item.transform.localPosition = new Vector3(0, -itemHeight * i, 0); itemList.Add(item); } // 更新 Content 的高度 contentRectTransform.sizeDelta = new Vector2(contentRectTransform.sizeDelta.x, itemHeight * (itemCount + 2)); // 更新子控件的内容 UpdateItemContent(); } void Update() { // 判断是否需要切换子控件 if (Input.GetKeyDown(KeyCode.UpArrow)) { currentItemIndex++; if (currentItemIndex > itemCount + 1) { currentItemIndex = 1; } UpdateItemContent(); } else if (Input.GetKeyDown(KeyCode.DownArrow)) { currentItemIndex--; if (currentItemIndex < 1) { currentItemIndex = itemCount + 1; } UpdateItemContent(); } } void UpdateItemContent() { // 更新子控件的内容 for (int i = 0; i < itemList.Count; i++) { int index = currentItemIndex - (itemList.Count - i - 1); if (index <= 0) { index += itemCount; } else if (index > itemCount) { index -= itemCount; } itemList[i].GetComponentInChildren<Text>().text = "Item " + index; } // 更新 Content 的位置 contentRectTransform.anchoredPosition = new Vector2(contentRectTransform.anchoredPosition.x, itemHeight * (currentItemIndex - 1)); } } ``` 这个示例代码实现了一个简单的无限循环 ScrollView,可以根据实际需求进行修改。其中,itemPrefab 是用于生成子控件的预制体,itemCount 是需要显示的子控件个数。在 UpdateItemContent 函数中,根据当前的 currentItemIndex 来更新子控件的内容,并将 Content 移动到对应的位置。在 Update 函数中,通过监听上下箭头键来模拟手指滑动,实现切换子控件的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值