首页 > 其他 > 详细

模仿小demo3.0

时间:2021-05-24 22:37:04      阅读:34      评论:0      收藏:0      [点我收藏+]

主要实现的是无限滚动功能(重点实现开始的自动添加与超出边界外的自动删除)如图gif.。

技术分享图片

脚本的挂载与预制体的拖放如下图所示:

技术分享图片

 

 LoopScrollView脚本如下:

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


public enum LoopScrollViewType {
    Horizontal,
    Vertical
}

public class LoopScrollView : MonoBehaviour
{

    #region 字段

    //这是一个预制体,通过对它(GameObject)实例化来创建它的子物体
    public GameObject childItemPrefab;

    public LoopScrollViewType scrollViewType = LoopScrollViewType.Vertical;

    private  GridLayoutGroup contentLayoutGroup;

    private  ContentSizeFitter sizeFitter;

    private  RectTransform content;

    private DataApdater<LoopDataItem> dataApdater; 

    #endregion


    #region Unity回调

    //对字段进行初始化 
    private void Awake()
    {
        Init();
    }

    private void Start()
    {
        //开始起到排序的作用
        contentLayoutGroup.enabled = true;
        sizeFitter.enabled = true;
        //添加一个子节点
        OnAddHead();
        //禁用,在0.1秒后开始禁用
        Invoke("EnableFalseGrid", 0.1f);
    }

    

    #endregion

    #region 方法

    //初始化
    public void Init()
    {
        content = transform.Find( "Viewport/Content").GetComponent<RectTransform>();
        if (content == null) { throw new System.Exception(" content 初始化失败"); }
        contentLayoutGroup = content.GetComponent<GridLayoutGroup>();
        if (contentLayoutGroup == null) { throw new System.Exception(" contentLayoutGroup 初始化失败"); } //判断是否获取到,若没有抛出异常
        sizeFitter = content.GetComponent<ContentSizeFitter >();
        if (sizeFitter == null) { throw new System.Exception(" sizeFitter 初始化失败"); }

        dataApdater = new DataApdater<LoopDataItem>();
        //-----------------------------------测试模拟的数据-------------------------------------------

        List<LoopDataItem> loopDataItems = new List<LoopDataItem>();

        for (int i = 0; i < 100; i++)
        {
            loopDataItems.Add(new LoopDataItem(i));
        }

        dataApdater.InitData(loopDataItems); //初始化数据,将loopDataItems传递过去
        //--------------------------------------------------------------------------------------
    }

    //获取一个子节点
    public GameObject GetChildItem() {    //此原理为 对象池原理

        //查找有没有 被回收 的子节点    (active 为 false 就是有被回收的子节点  ???)

        for (int i = 0; i < content.childCount ; i++)   //for循环遍历出所有的子节点,判断所有的子节点有没有被回收
        {
            if (!content.GetChild(i).gameObject.activeSelf)      //!取反    所以现在状态是隐藏状态 
            {
                content.GetChild(i).gameObject.SetActive(true);
                return content.GetChild(i).gameObject;
            }
        }

        //如果没有 创建一个
        GameObject childItem = GameObject.Instantiate(childItemPrefab, content.transform);   //实例化一个游戏物体   子物体是childItemPrefab 父物体是content.transform
        childItem.transform.localScale = Vector3.one; //简单设置数据       大小、位置
        childItem.transform.localPosition = Vector3.zero;
        childItem.GetComponent<RectTransform>().anchorMin = new Vector2(0, 1); //设置锚点
        childItem.GetComponent<RectTransform>().anchorMax = new Vector2(0, 1);
        childItem.GetComponent<RectTransform>().sizeDelta = contentLayoutGroup.cellSize; //子物体 设置宽高

        LoopItem loopItem = childItem.AddComponent<LoopItem>();
        loopItem.onAddHead += this.OnAddHead;                        //???
        loopItem.onRemoveHead += this.OnRemoveHead;
        loopItem.onAddLast += this.OnAddLast;
        loopItem.onRemoveLast += this.OnRemoveLast;

        loopItem.SetLoopScrollViewType(this.scrollViewType);

        return childItem ;
    }

    //在上面添加一个物体
    public void OnAddHead() {

        LoopDataItem loopDataItem = dataApdater.GetHeaderData();     //获取同步数据

        if ( loopDataItem != null )
        {
            Debug.Log("添加头");
            Transform first = FindFirst();

            GameObject obj = GetChildItem();     //获取到一个子节点
            obj.transform.SetAsFirstSibling();   //把它放在第一个位置
            SetData(obj, loopDataItem);          //设置数据
            //动态设置位置
            if (first != null)
            {
                switch (scrollViewType)
                {
                    case LoopScrollViewType.Horizontal:
                        obj.transform.localPosition = first.localPosition - new Vector3(contentLayoutGroup.cellSize.x + contentLayoutGroup.spacing.x, 0 , 0);
                        break;
                    case LoopScrollViewType.Vertical:
                        obj.transform.localPosition = first.localPosition + new Vector3(0, contentLayoutGroup.cellSize.y + contentLayoutGroup.spacing.y, 0);
                        break;

                }

            }
        }

    }

    //移除当前最上面的物体
    public void OnRemoveHead() {
        Debug.Log("移除头");

        if (dataApdater.RemoveHeadData())
        {
            Transform first = FindFirst();
            if (first != null)
            {
                first.gameObject.SetActive(false);
            }
        }
    }

    //在最后加一个物体
    public void OnAddLast() {

        LoopDataItem loopDataItem = dataApdater.GetLastData(); //获取尾部数据
        if (loopDataItem != null)
        {
            Debug.Log("添加尾");
            Transform last = FindLast();

            GameObject obj = GetChildItem();     //获取到一个子节点
            obj.transform.SetAsLastSibling();   //把它放在第一个位置
            SetData(obj, loopDataItem);         //设置数据 

            switch (scrollViewType)
            {
                case LoopScrollViewType.Horizontal:

                    if (last != null)                   //动态设置位置
                    {
                        obj.transform.localPosition = last.localPosition + new Vector3(contentLayoutGroup.cellSize.x + contentLayoutGroup.spacing.x, 0 , 0);
                    }

                    //要不要增加高度
                    if (IsNeedAddContentHeight(obj.transform))
                    {
                        //对高度进行增加
                        content.sizeDelta += new Vector2(contentLayoutGroup.cellSize.x + contentLayoutGroup.spacing.x,0 );    //高度加间距
                    }

                    break;
                case LoopScrollViewType.Vertical:

                    if (last != null)                   //动态设置位置
                    {
                        obj.transform.localPosition = last.localPosition - new Vector3(0, contentLayoutGroup.cellSize.y + contentLayoutGroup.spacing.y, 0);
                    }

                    //要不要增加高度
                    if (IsNeedAddContentHeight(obj.transform))
                    {
                        //对高度进行增加
                        content.sizeDelta += new Vector2(0, contentLayoutGroup.cellSize.y + contentLayoutGroup.spacing.y);    //高度加间距
                    }

                    break;

            }


        }
    }

    //移除最后一个物体
    public void OnRemoveLast() {

        if ( dataApdater.RemoveHeadData()) {
            Debug.Log("移出尾");
            Transform last = FindLast();
            if (last != null)
            {
                last.gameObject.SetActive(false);
            }
        }
    }

    //找到第一个游戏物体的位置
    public Transform FindFirst() {

        for (int i = 0; i < content.childCount; i++)
        {
            if (content.GetChild(i).gameObject.activeSelf)
            {
                return content.GetChild(i);
            }
        }
        return null;
    }

    //找到最后一个游戏物体的位置
    public Transform FindLast() {
        for (int i = content.childCount - 1; i >= 0 ; i--)
        {
            if (content.GetChild(i).gameObject.activeSelf)
            {
                return content.GetChild(i);
            }
        }
        return null;
    }

    //禁用 contentLayoutGroup sizeFitter 这两个组件
    public void EnableFalseGrid() {
        contentLayoutGroup.enabled = false;
        sizeFitter.enabled = false;
    }

    //是不是需要增加 Content 的高度  ,类似于判断边界
    public bool IsNeedAddContentHeight( Transform trans ) {
        Vector3[] rectCorners = new Vector3[4];
        Vector3[] contentCorners = new Vector3[4];
        trans.GetComponent<RectTransform>().GetWorldCorners(rectCorners);
        content.GetWorldCorners(contentCorners);

        switch (scrollViewType)
        {
            case LoopScrollViewType.Horizontal:
                if (rectCorners[3].x < contentCorners[3].x)
                {
                    return true;
                }
                break;
            case LoopScrollViewType.Vertical:
                if (rectCorners[0].y < contentCorners[0].y)
                {
                    return true;
                }
                break;
        }



        return false;
    }

    //设置数据
    public void SetData(GameObject chileItem,LoopDataItem data)
    {
        chileItem.transform.Find("Text").GetComponent<Text>().text = data.id.ToString();

    }
    #endregion
}

 

 ???现存问题:

在往下拖动时,可以实现新项目的增加,但是将滚动条拖回到开头,消失的没有再出现。emmmm,不知道哪里出现了问题,不知道如何解决。

 

脚本:LoopItem有部分更新,在复制一次

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

public class LoopItem : MonoBehaviour
{
    #region 字段

    //通过一个方法,获取到边界的信息
    private RectTransform rect;       //rect  自己的显示区域
    private RectTransform viewRect;   //viewRect 是scrollview的显示区域

    //声明四个边的坐标
    private Vector3[] rectCorners;
    private Vector3[] viewCorners;

    private LoopScrollViewType scrollViewType;

    #endregion



    #region 事件

    public Action onAddHead;
    public Action onRemoveHead;
    public Action onAddLast;
    public Action onRemoveLast;

    #endregion


    //对两个变量初始化
    private void Awake()
    {
        rect = transform.GetComponent<RectTransform>();  //通过transf.GetComponent 获取到自己的显示区域
        viewRect = transform.GetComponentInParent<ScrollRect>().GetComponent<RectTransform>();
        rectCorners = new Vector3[4];                    //初始化数组
        viewCorners = new Vector3[4];
    }

    void Update()
    {
        LinstenerCorners();
    }


    //监听边界
    public void LinstenerCorners() {
        //获取自身的边界
        rect.GetWorldCorners(rectCorners);
        //获取显示区域的边界
        viewRect.GetWorldCorners(viewCorners);

        if (IsFirst())                 //如果是头
        {
            switch (scrollViewType)
            {
                case LoopScrollViewType.Horizontal:

                    if (rectCorners[3].x < viewCorners[0].x)
                    {
                        //把头节点给隐藏掉
                        if (onRemoveHead != null) { onRemoveHead(); }     //如果它不为空,触发onRemoveHead
                    }
                    if (rectCorners[0].x > viewCorners[0].x)
                    {
                        //添加头节点
                        if (onAddHead != null) { onAddHead(); }
                    }

                    break;

                case LoopScrollViewType.Vertical:

                    if (rectCorners[0].y > viewCorners[1].y)
                    {
                        //把头节点给隐藏掉
                        if (onRemoveHead != null) { onRemoveHead(); }     //如果它不为空,触发onRemoveHead
                    }
                    if (rectCorners[1].y < viewCorners[0].y)
                    {
                        //添加头节点
                        if (onAddHead != null) { onAddHead(); }
                    }

                    break;
            }


        }

        if (IsLast())              //如果是尾
        {

            switch (scrollViewType)
            {
                case LoopScrollViewType.Horizontal:
                    //添加尾部
                    if (rectCorners[3].x < viewCorners[3].x)
                    {
                        if (onAddLast != null) { onAddLast(); }
                    }
                    //回收尾部
                    if (rectCorners[0].x > viewCorners[3].x)
                    {
                        if (onRemoveHead != null) { onRemoveHead(); }
                    }
                    break;

                case LoopScrollViewType.Vertical:
                    //添加尾部
                    if (rectCorners[0].y > viewCorners[0].y)
                    {
                        if (onAddLast != null) { onAddLast(); }
                    }
                    //回收尾部
                    if (rectCorners[1].y < viewCorners[0].y)
                    {
                        if (onRemoveHead != null) { onRemoveHead(); }
                    }
                    break;
            }

        }



    }



    //判断是不是第一个
    public bool IsFirst() {

        for (int i = 0; i < transform.parent.childCount; i++)   //计算子物体的数量,头部从0开始找
        {
            if (transform.parent.GetChild(i).gameObject.activeSelf)   //依次获取子物体,并判断是否是显示状态
            {
                if (transform.parent.GetChild(i) == transform)       //判断是不是它自己
                {
                    return true;
                }
                break;
            }
        }

        return false;
    }

    //是不是最后一个
    public bool IsLast() {

        for (int i = transform.parent.childCount - 1; i >= 0; i--)   //计算子物体的数量,尾部从后往前找
        {
            if (transform.parent.GetChild(i).gameObject.activeSelf)   //依次获取子物体,并判断是否是显示状态
            {
                if (transform.parent.GetChild(i) == transform)       //判断是不是它自己
                {
                    return true;
                }
                break;
            }
        }
        return false;
    }

    //设置LoopScrollViewd 的类型
    public void SetLoopScrollViewType( LoopScrollViewType loopScrollViewType ) {
        this.scrollViewType = loopScrollViewType;
    }
}

 

数据库搭建脚本:

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

public class DataApdater<T>
{
    #region 字段

    //保存的所有数据
    public List<T> allData = new List<T>();    //不知道什么类型  所以是泛型
    //保存当前正在显示的数据
    public LinkedList<T> currentShowData = new LinkedList<T>();   //LinkedList 链表

    #endregion


    #region 方法

    public T GetHeaderData()
    {
        //判断总数据的数量
        if (allData.Count == 0) {
            return default(T);
        }
        //特殊情况
        if ( currentShowData.Count == 0 )
        {
            T header = allData[0];
            currentShowData.AddFirst(header);
            return header;
        }

        //currentShowData 当前正在显示的第一个数据
        T t = currentShowData.First.Value;
        int index = allData.IndexOf(t);
        if (index != 0)                          //判断数据不为空时
        {
            T header = allData[index - 1];
            currentShowData.AddFirst(header);    //将数据加到头部
            return header;
        }

        return default(T);                       //如果等于零,默认值为空
    }

    public bool RemoveHeadData() {
        //移除 currentShowData 当前正在显示的第一个数据的第一个数据
        if (currentShowData.Count == 0)
        {
            return false;
        }
        currentShowData.RemoveFirst();
        return true;
    }

    public T GetLastData() {
        //判断总数据的数量
        if (allData.Count == 0)
        {
            return default(T);
        }
        //特殊情况
        if (currentShowData.Count == 0 || currentShowData.Count == 1)
        {
            T l = allData[0];
            currentShowData.AddLast(l);
            return l;
        }

        //获取 currentShowData 最后一个的下一个

        T last = currentShowData.Last.Value;
        int index = allData.IndexOf(last);

        if (index != allData.Count - 1)
        {
            T now_last = allData[index + 1];
            currentShowData.AddLast(now_last);
            return now_last;
        }
        return default(T);
    }

    public bool RemoveLastData() {
        //移除 currentShowData 最后一个
        if (currentShowData.Count == 0 || currentShowData.Count == 1) { return false; }
        currentShowData.RemoveLast();
        return true;
    }

    #endregion

    #region 数据管理

    public void InitData(T[] t) {
        allData.Clear();
        currentShowData.Clear();
        allData.AddRange(t);
    }

    public void InitData(List<T> t  )  //重载函数
    {
        InitData(t.ToArray());
    }

    public void AddData(T[] t)          //添加数据
    {
        allData.AddRange(t);
    }

    public void AddData(List<T> t)
    {
        AddData(t.ToArray());
    }
    #endregion

}

 

调取数据库(好像是)的脚本:

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

public class LoopDataItem 
{

    public int id;

    public LoopDataItem(int id)
    {
        this.id = id;
    }

}

 

模仿小demo3.0

原文:https://www.cnblogs.com/gyjldlhiahia/p/14805785.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!