• 制作一个简易的UGUI无限滑动框(Unity)


    市面上有很多这种无限拖拽的插件 但是功能细化的太严重了 改的话有些耗时 如果没有太多严苛的需求没必要改工程量比较大的插件 完全可以自己写一个 

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    public class ItemRect
    {
        public Rect mRect;
    
        //格子的索引 因为是用来关联位置的 所以不存在删除或修改操作
        public readonly int itemIndex;
    
        //如果这个位置上存在显示的UI那么将格子的isUse属性设置为true
        public bool isUse = false;
    
        public ItemRect(float x,float y,float width,float height,int itemIndex)
        {
            mRect = new Rect(x,y,width,height);
            this.itemIndex = itemIndex;
        }
    
    }
    
    
    public interface IItemExecute {
    
        //刷新数据
        void Execute_FinshData();
        //绑定UI
        void BindUgui();
    }
    
    //抽象任何地方出每一个滑动单元的公共方法
    public class ItemPropoty : MonoBehaviour, IItemExecute
    {
        //储存的数据
        public object Data;
        public object DataKey;
    
        public ItemRect mDRect;
    
        public delegate void FreshFunc(int index);
        //在初始化的时候给到刷新数据的方法 
        public FreshFunc onUpdata;
    
    
        public virtual void Execute_FinshData() {
            BindUgui();
            if (onUpdata != null)
                onUpdata(mDRect.itemIndex);
        }
    
        public void Execute_PlacePos()
        {
            (transform as RectTransform).sizeDelta = mDRect.mRect.size;
            (transform as RectTransform).localPosition = mDRect.mRect.position;
            BindUgui();
        }
    
        /// <summary>
        /// 绑定UGUI 用名称查询的方式绑定UI类索引
        /// </summary>
        public virtual void BindUgui() {
    
        }
    
    }
    
    
    /// <summary>
    /// 定制滑动单元
    /// </summary>
    

      这个用来给定格子位置  在初始化的时候就确认所有格子的位置 这样不用动态的计算 

      这里我把那个UI的绑定设计为 尽量使用名字去查询UI 在bindUgui中去查询和添加按钮还有一些其他的回调

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class ItemUnit_1 : ItemPropoty
    {
        Button btn;
        Text txt;
    
        public override void BindUgui()
        {
            btn = transform.GetComponentInChildren<Button>();
            txt = transform.GetComponentInChildren<Text>();
            txt.text = mDRect.itemIndex.ToString();
            btn.onClick.AddListener(() => {
                txt.text = mDRect.itemIndex + " : " + mDRect.itemIndex;
    
            });
    
        }
    
        public void AddImage() {
        }
        public void ChangeImage() {
    
        }
    }
    

      

    把ItemPropoty放在拖拽的单元上面
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using System.Linq;
    public class CustomScroll : MonoBehaviour
    {
        //用于显示的单元
        public GameObject itemUnit;
        private RectTransform item;
        //这个用来存储UI显示的实例
        private List<Transform> itemList;
        //记录所有ui应该显示的位置
        private Dictionary<int, ItemRect> itemRectDic;
    
    
        //显示的画布
        public RectTransform mDisplayRect;
    
        //上拉是否还有显示单位
        private bool? isHavePrevious = null;
        public bool? IsHavePrevious { get => isHavePrevious; }
    
        //下拉是否还有显示单位
        private bool? isHaveSubsequent = null;
        public bool? IsHaveSubsequent { get => isHaveSubsequent; }
    
    
        private ScrollRect scrollRect;
    
        //显示的行数  需要计算
        private int scrollRow;
        public int ScrollRow { get => scrollRow; }
    
        [Header("行间隙")]
        public float interval_Veritical;
        public float Interval_Veritical { get => interval_Veritical + item.rect.height; }
    
        //显示的列数
        private int scrollColumn;
        public int ScrollColumn { get => scrollColumn; }
    
        [Header("列间隙")]
        public float interval_Horizontal;
        public float Interval_Horizontal { get => interval_Horizontal + item.rect.width;}
    
        void Start()
        {
            IntializeScroll();
            IntializeDisplayCount(2, 30);
    
            //TODO FIX 测试用
            isLoad = true;
        }
    
        //做一些初始化的任务
        private void IntializeScroll()
        {
            itemList = new List<Transform>();
    
            waitChangeList = new List<ItemPropoty>();
    
            //GetComponentInParent<ScrollRect>().movementType = ScrollRect.MovementType.Clamped;
    
            scrollRect = GetComponentInParent<ScrollRect>();
    
            itemRectDic = new Dictionary<int, ItemRect>();
            item = itemUnit.transform as RectTransform;
    
            //初始化位置记录
            previousPagePos = transform.position.y;
    
        }
    
        /// <summary>
        /// 先计算出显示的行列
        /// </summary>
        public void IntializeDisplayCount(int columnCount, int itemCount) {
            SetContentSizeDelta(columnCount, itemCount);
            IntializeDisplayCount(scrollRow, scrollColumn, itemCount);
        }
    
        private void SetContentSizeDelta(int columnCount, int itemCount) {
            //若乱设置0或复负数直接按1处理
            scrollColumn = Mathf.Max(1, columnCount);
            //计算出显示的最大行数  item的长度
            scrollRow = itemCount / scrollColumn + 1;
            //设置显示画布的大小
            (transform as RectTransform).sizeDelta = new Vector2(
                scrollColumn * Interval_Horizontal,
                (scrollRow - 1) * Interval_Veritical);
        }
    
        //
        public void IntializeDisplayCount(int rowCount,int columnCount, int itemCount)
        {
            //先初始化好格子的位置 在初始化位置的同时设置好显示内容的尺寸
            AddItemPosition(itemCount);
            //计算当前显示的数量 存在一个占半格子的问题 暂时未处理 可以使用rect.Overlaps 查看是否相交 根据相交的位置判断加的排数
            var pageSize = (transform.parent.parent as RectTransform).sizeDelta;
            //因为间距的问题没处理所以临时加2
            var pageArea = (int)(pageSize.x / Interval_Horizontal)* (int)(pageSize.y / Interval_Veritical);
            //TODO FIX
            int maxDsiplayNum = (int)pageArea + scrollColumn+2;
                
            //Debug.Log("当前最大的显示数量 : "+maxDsiplayNum);
    
            for (int i = 0; i < scrollRow && i< maxDsiplayNum; i++)
            {
               
                Transform tmpItem = GameObject.Instantiate(itemUnit).transform;
                itemList.Add(tmpItem);
                tmpItem.localScale = Vector3.one;
                tmpItem.SetParent(transform, false);
                //tmpItem.gameObject.AddComponent<ItemUnit_1>();
                //tmpItem.gameObject.layer = mDisplayRect.gameObject.layer;
                tmpItem.gameObject.SetActive(false);
                tmpItem.name = i.ToString();
            }
            BenginDisplay();
        }
    
        /// <summary>
        /// 开始显示UI单元
        /// </summary>
        public void BenginDisplay() {
            //标记显示头
            for (int i = 0; i < itemRectDic.Count; i++)
            {
                if (i == itemList.Count)
                    break;
              
                //得到物体身上的属性
                var tmp = itemList[i].GetComponent<ItemPropoty>();
                //拿到对应的值
                itemRectDic.TryValueGet(i,out tmp.mDRect);
                //给定位置
                tmp.Execute_PlacePos();
                //将他设置为可见
                itemList[i].gameObject.SetActive(true);
    
                tmp.mDRect.isUse = true;
    
            }
        }
    
    
        private int allItemCount = 0;
        public int AllItemCount { get => allItemCount; }
    
        [Header("x,y轴的初始边距")]
        public int distance_x = 100;
        public int distance_y = -50;
    
        /// <summary>
        /// 判断元数是否为追加
        /// </summary>
        /// <param name="isSuperaddition"></param>
        public void AddItemPosition(int nxtCount) {
    
            int curRow,curColumn;
            int tmp = itemRectDic.Count;
            allItemCount += nxtCount;
            SetContentSizeDelta(scrollColumn,allItemCount);
            for (int i = tmp; i < tmp + nxtCount; i++)
            {
                curRow = i / scrollColumn;
                curColumn = i % scrollColumn;
                var itemPos = new ItemRect(curColumn*Interval_Horizontal + distance_x,
                    -curRow*Interval_Veritical + distance_y,
                    item.rect.width,
                    item.rect.height,
                    i);
                itemRectDic.Add(i,itemPos);
            }
        }
    
    
        /// <summary>
        /// 查询所有排位置的最值元素 
        /// 做个优化 查询每一排的第一个元素就行了  这样换排的时候 如果是上一排就用index-1 下一排就是 index+scrollColumn(列数)
        /// </summary>
        /// <param name="isMax"></param>
        /// <returns></returns>
        public Transform GetBestValueItem(bool isMax)
        {
            if (itemList == null)
            {
                return null;
            }
            Transform bestTmp = itemList[0];
            for (int i = 0; i < itemList.Count; i += scrollColumn)
            {
    
                bool result = isMax ? bestTmp.position.y < itemList[i].position.y 
                    : bestTmp.position.y > itemList[i].position.y;
                if (result)
                {
                    bestTmp = itemList[i];
                }
            }
           // Debug.Log(bestTmp.name);
            return bestTmp;
        }
    
        /// <summary>
        /// 查询最值同时把index传出去
        /// </summary>
        /// <param name="isMax"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public Transform GetBestValueItem(bool isMax,out int index)
        {
            var tmp = GetBestValueItem(isMax);
            index = tmp.GetComponent<ItemPropoty>().mDRect.itemIndex;
            return tmp;
        }
    
    
    
    
        private List<ItemPropoty> waitChangeList;
        /// <summary>
        /// 查找到元素Y轴位置最小的元素
        /// </summary>
        /// <param name="item"></param>
        private void ExecuteChangeLocation(Transform item,bool isDown)
        {
            Vector3 minItemPosition = GetBestValueItem(!isDown).position;
           
            //开始收集同排的元素位置
            for (int i = 0; i < itemList.Count; i++)
            {
                if (itemList[i].Comparer_Position_Y(item.position)) {
                    waitChangeList.SortAdd(itemList[i].GetComponent<ItemPropoty>());
                }
            }
            ChangeLineLocation(isDown);
        }
    
        /// <summary>
        /// 根据刷新方式做出更改位置操作
        /// </summary>
        /// <param name="isDown"></param>
        private void ChangeLineLocation(bool isDown) {
            if (waitChangeList == null || waitChangeList.Count == 0) {
                Debug.LogError("翻车了: ChangeLineLocation 查询失败");
                return;
            }
    
            //拿到当前最低/高位置的格子
            var bestValue = GetBestValueItem(!isDown).GetComponent<ItemPropoty>();
            
            #region
            /*
            int listIndex,itemIndex;
            for (int i = 0; i < waitChangeList.Count; i++) {
                if (isDown)
                {
                    itemIndex = bestValue.mDRect.itemIndex + i + scrollColumn;
                    listIndex = i;
                }
                else
                {
                    itemIndex = bestValue.mDRect.itemIndex - (waitChangeList.Count - 1);
                    listIndex = scrollColumn - (waitChangeList.Count - i);
                }
    
                if (itemRectDic.ContainsKey(itemIndex))
                {
    
                    //当往下显示的时候将上边隐藏部位的格子释放掉
                    waitChangeList[listIndex].mDRect.isUse = false;
                    //查询到左下角的首元素加上列数个单位之后 正好是下一行的位置
                    waitChangeList[listIndex].mDRect = itemRectDic[itemIndex];
                    waitChangeList[listIndex].Execute_PlacePos();
                    //标记新的占用位置
                    waitChangeList[listIndex].mDRect.isUse = true;
                }
            }
             */
            #endregion
    
            if (isDown)
            {
                //Debug.Log("发现最底面的格子 : "+bestValue.name);
                for (int i = 0; i < waitChangeList.Count; i++)
                {
                    //当再次刷新出现越界的时候    就是画布底面已经没有位置了不需要UI再往下刷新了 跳出去
                    if (itemRectDic.ContainsKey(bestValue.mDRect.itemIndex + i + scrollColumn))
                    {
    
                        //当往下显示的时候将上边隐藏部位的格子释放掉
                        waitChangeList[i].mDRect.isUse = false;
                        //查询到左下角的首元素加上列数个单位之后 正好是下一行的位置
                        waitChangeList[i].mDRect = itemRectDic[bestValue.mDRect.itemIndex + i + scrollColumn];
                        waitChangeList[i].Execute_PlacePos();
                        //标记新的占用位置
                        waitChangeList[i].mDRect.isUse = true;
                    }
                }
            }
            else
            {                                                                     
                for (int i = scrollColumn; i > 0 ; i--)
                {
                    //Debug.Log("获取的值"+(bestValue.mDRect.itemIndex - i));
                    //当再次刷新出现越界的时候    就是画布底面已经没有位置了不需要UI再往下刷新了 跳出去
                    if (itemRectDic.ContainsKey(bestValue.mDRect.itemIndex - i))
                    {
                        //Debug.Log("拾取格子"+(bestValue.mDRect.itemIndex - i));
                        //当往下显示的时候将上边隐藏部位的格子释放掉
                        waitChangeList[scrollColumn -i].mDRect.isUse = false;
                        //查询到左下角的首元素加上列数个单位之后 正好是下一行的位置
                        waitChangeList[scrollColumn - i].mDRect = itemRectDic[bestValue.mDRect.itemIndex -i];
                        waitChangeList[scrollColumn - i].Execute_PlacePos();
                        //标记新的占用位置
                        waitChangeList[scrollColumn - i].mDRect.isUse = true;
                    }
                }
            }
    
            //改完格子清理表
            waitChangeList.Clear();
        }
    
        //TODO fix
        /// <summary>
        /// 检测元素是否越界 目前的策略 用每排第一个元素进行Y轴判断移动位置
        /// 所有的元素位置都保存在字典里 找出越界的位置的刷新那一排的位置
        /// 判断元素向上越界还是向下越界
        /// </summary>
        /// <returns></returns>
        private Transform CheckCrossTheBorder(bool isDown)
        {
            //Debug.Log("我传入的值 : " + isDown);
            var tmpMaxItem = GetBestValueItem(isDown);
          //Debug.Log(tmpMaxItem.position.y+"     " + mDisplayRect.position.y);
            if(isDown)
                return tmpMaxItem.position.y > mDisplayRect.position.y ? tmpMaxItem : null;
    
            float pageBottom = mDisplayRect.position.y - mDisplayRect.sizeDelta.y;
            //Debug.Log("查询最小的位置 : "+pageBottom +"                     "+ mDisplayRect.position.y);
    
            return tmpMaxItem.position.y < pageBottom ? tmpMaxItem : null;
           
        }
    
        //查看当前的滑动方向
        private float previousPagePos;
        private bool? isSlideDown;
        // 当isSlideDown为空是表明当前无操作
        public bool? IsSlideDown { get => isSlideDown;}
    
    
        /// <summary>
        /// 查看当前滑动的方向 如果页面向上滑动那么Y轴是增加的 反之下滑  如果为空那么就是未参与滑动操作
        /// </summary>
        /// <returns></returns>
        private void CheckCurDirection() {
            if (transform.position.y == previousPagePos) {
                isSlideDown = null;
                return;
            }
            isSlideDown = transform.position.y < previousPagePos;
            previousPagePos = transform.position.y;
        }
    
    
    
        private void Update()
        {
            CheckCurDirection();
            if (isSlideDown == null)
                return;
    
            bool isTrue = (bool)isSlideDown;
            var changeItem = CheckCrossTheBorder(!isTrue);
            if (changeItem != null)
                ExecuteChangeLocation(changeItem, !isTrue);
    
    
            //TODO FIX 这里需要严重优化 暂时没考虑好具体方法
            if (!isTrue) {
                //Debug.Log("内容框底部点 : "+(transform.position.y-(transform as RectTransform).sizeDelta.y));
                //Debug.Log("显示框底部点 : "+(mDisplayRect.position.y-(mDisplayRect as RectTransform).sizeDelta.y));
                var contentPos = (transform.position.y - (transform as RectTransform).sizeDelta.y);
                var displayPos = (mDisplayRect.position.y - (mDisplayRect as RectTransform).sizeDelta.y);
    
                //TODO FIX 临时测试代码
                if (contentPos - displayPos > 80) {
                    if (isLoad) {
                        transform.parent.parent.GetChild(1).GetComponent<loadState>().SetLoadBar(true);
                        StartCoroutine(LoadItem(20));
                        isLoad = false;
    
                    }
    
                }
            }
    
    
        }
    
    
    
    
        //-----------------------------------------加载模块拓展----------------------------------------
        private bool isLoad =true;
    
    
        //是否结束异步等待
        public bool isAsyncWait = true;
    
        private IEnumerator LoadItem(int count)
        {
            while (isAsyncWait)
            {
                yield return new WaitForSeconds(0.3f);
            }
    
            //TODO FIX 测试用
            AddItemPosition(count);
            if (allItemCount > 80)
                isLoad = false;
    
            isAsyncWait = true;
        }
    
        //TODO FIX 测试用
      
    }
    
    internal static class ExetenceMethod_YC
    {
        //允许三个单位的误差
        public static bool Comparer_Position_Y(this Transform my,Vector3 other)
        {
            if (my.position.y > other.y - 3 && my.position.y < other.y + 3)
                return true;
            return false;
        }
    
        public static void TryValueGet<TKey,TValue>(this Dictionary<TKey, TValue> my, TKey key, out TValue value) {
            if (!my.ContainsKey(key)) {
                Debug.LogError("键为空检测触发处 : Key = "+key);
                value = default(TValue);
                return;
            }
            my.TryGetValue(key,out value);
        }
    
        public static void SortAdd<T>(this IList<T> list,T TObj,bool isDescending = false) {
            if (typeof(T) == typeof(ItemPropoty)) {
                if (list.Count == 0)
                {
                    list.Add(TObj);
                    return;
                }
                int insertIndex = isDescending ? 0 : list.Count;
                if ((TObj as ItemPropoty).mDRect.itemIndex < (list[0] as ItemPropoty).mDRect.itemIndex)
                {
                    list.Insert(list.Count - insertIndex, TObj);
                }
                else {
                    list.Insert(insertIndex, TObj);
                }
            }
        }
    
        //关于处理间距的问题 计算时存再行列的数量误差 去掉最后一排的间距再进行计算 
        /*
              ----------------------------------------问题清单------------------------------------------------
              1,快速拖拽会发生丢失UI现象
              2,计算行列自动布局显示半个UI的优化
              3,拓展加载模块功能
              4,优化下拉判断选项
         */
    }
    

      这里还有一些存在的问题...   现在懒得动 以后改...      有哪位朋友使用的话可以联系我qq : 973407312

    using System.Collections;
    using System.Collections.Generic;
    using System.Text;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class loadState : MonoBehaviour
    {
        public Text[] dot;
    
        public bool isLoading;
        
        public CustomScroll cs;
    
        private float nxtTime = 0;
        public void SetDot()
        {
    
        }
    
    
        public void SetLoadBar(bool isTrue)
        {
            isLoading = isTrue;
            gameObject.SetActive(true);
            StartCoroutine(Recover());
    
        }
    
    
        public void BarFadeAway()
        {
    
        }
    
        private int index=0;
        public void whirl() {
            index++;
            index %= dot.Length;
            StartCoroutine(Bright());
        }
    
    
        private IEnumerator Bright() {
            dot[index].gameObject.SetActive(false);
            yield return new WaitForSeconds(0.18f);
            dot[index].gameObject.SetActive(true);
    
        }
    
        private IEnumerator Recover()
        {
            yield return new WaitForSeconds(2);
            cs.isAsyncWait = false;
            gameObject.SetActive(false);
            isLoading = false;
        }
    
    
    
        void Update()
        {
            if (isLoading)
            {
                
                nxtTime += Time.deltaTime;
                if (nxtTime >= 0.2f) {
                    whirl();
                    nxtTime = 0;
                }
            }
        }
    }
    

      可以像这样 制作一个加载提示条  这里弄的有些简陋  可以自己弄个Dotween做个渐入渐出 多给几条提示信息

      demo下载:链接:https://pan.baidu.com/s/1XS6wWAoaYWCA2GbgQUf4CQ 

      提取码:ms5r 

  • 相关阅读:
    4-6 R语言函数 排序
    微信公众平台开发接口之天气预报字符串截取
    微信公众平台开发接口之百度翻译
    手机模板 好
    模板1
    手机模板2
    手机模板1
    淘宝素材装修
    如何批量删除SQL注释?
    (3.14)mysql基础深入——mysql 日志分析工具之pt-querty-digest【待完善】
  • 原文地址:https://www.cnblogs.com/chenggg/p/10847581.html
Copyright © 2020-2023  润新知