深度探究Unity中的ScrollView(附上扩展ScrollView代码)

一:Scroll View下重要组件属性及API的详解

Scroll View是一个组合UI,创建后有以下内容
深度探究Unity中的ScrollView(附上扩展ScrollView代码)_第1张图片
——Viewport:显示的区域,一般配合Mask一起使用
——Scrollbar Horizontal:水平滑动条
——Scrollbar Vertical:竖直滑动条



Scroll View上最重要的就是Scroll Rect组件:
深度探究Unity中的ScrollView(附上扩展ScrollView代码)_第2张图片
——Content:所有要显示的内容(可滑动的区域)
——Horizontal:是否可以水平滑动
——Vertical:是否可以竖直滑动
——Movement Type:滑动的类型(不受限制随意滑动,到边界出现弹性,到边界没有弹性)

——Elasticity:弹性数值
——Inertia:是否用惯性
——Deceleration Rate:惯性的加速度
——Viewport:可视区域,一般配合Mask一起使用

 


获取content的宽和高:

//获取content的宽和高(content矩形的width和height)
print("size " + content.rect.size);
print("width " + content.rect.width);
print("height " + content.rect.height);
print("width " + content.rect.size.x);
print("height " + content.rect.size.y);


//以下方法本质上并不是获取content的宽和高,而是获取content矩形的四个角的位置
print("width " + content.rect.xMax);
print("width " + content.rect.xmin);
print("height " + content.rect.ymax);
print("height " + content.rect.ymin);

//设置content的大小——content.sizeDelta
print("相对位置 "+content.sizeDelta);
//——content.sizeDelta是一个相对大小,直接设置位置即可,不需要累加上之前content的数值
//——content.rect.size是结构体类型,不能改数值

二:搭配ScrollView的组件

——Grid Layout Group
深度探究Unity中的ScrollView(附上扩展ScrollView代码)_第3张图片
Padding:Content下所有物体对于Content整体的上下左右偏移量
Cell Size:Content下所有物体的大小(持有Grid Layout Group组件下所有Image的RectTransfrom大小设置都被锁上,只能通过此属性设置Image大小)
Spacing:Content下所有物体的行与列的间隔
Start Corner:填充的起始点
Start Axis:填充的轴向
Child Alignment:所有子物体的对齐方式
Constraint:限制Content内容的行数和列数

 

 

——Vertical Layout Group和Horizontal Layout Group
深度探究Unity中的ScrollView(附上扩展ScrollView代码)_第4张图片
Padding:Content下所有物体对于Content整体的上下左右偏移量
Spacing:Content下所有物体的行或列的间隔
Child Alignment:所有子物体的对齐方式
Control Child Size:是否控制子物体的大小(勾选后将不能控制其大小)
Use Child Scale:是否使用子物体的缩放
Child Force Expand:子物体自动伸展(Width和Height一般都不勾选)

 


——Content Size Fitter(此组件还可以制作提示文本框随着文字的数量增多而变大)

Horizontal Fit:水平的自动调节(通常选择Preferred Size)
Vertical Fit:竖直的自动调节(通常选择Preferred Size)


三:如何计算ScrollView的Content大小

深度探究Unity中的ScrollView(附上扩展ScrollView代码)_第5张图片
content.sizeDelta = (n-1)*(Spacing X+CellSize X)


四:分页展示(两种方式实现单页滑动+多页滑动)

 

——通过horizontalNormalizedPosition实现(推荐使用)

using System;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.UI;

public class ExtensionScrollView : MonoBehaviour, IBeginDragHandler, IEndDragHandler
{
    [Header("能否移动多页")]
    public bool canMoveMult; //能否移动多页

    private ScrollRect _scrollRect; //ScrollRect组件
    private RectTransform content; //ScrollRect组件的Content

    //==========ScrollRect的Content的一些参数
    private float cellsizeX; //格子的长度X
    private float spacingX; //间隔X

    private int totalPage; //总页数
    private int pageIndex; //当前页的下标
    private float[] pagelNormalizedPositon; //每页的NormalizedPositon的值
    private float targetNormalizedPosition; //目标的NormalizedPositon的值

    private float beginMousePosX; //鼠标开始按下的位置X
    private float endMousePosX; //鼠标结束按下的位置X
    private bool isDrag; //是否在拖拽

    [Header("缓动到目标页的时间")]
    public float time; //平滑时间

    private float factor; //比例因数

    [FormerlySerializedAs("moveOnePageDis")]
    [Header("移动一页的距离")]
    [Header("==========")]
    public float moveOnePageDis; //移动一页的距离

    [FormerlySerializedAs("moveMultPageDis")]
    [Header("移动多页的距离")]
    public float moveMultPageDis; //移动多页的距离

    private void Awake()
    {
        _scrollRect = GetComponent();
        content = _scrollRect.content;
        totalPage = content.transform.childCount;
        cellsizeX = content.GetComponent().cellSize.x;
        spacingX = content.GetComponent().spacing.x;

        //动态设置Content的大小:sizeDelta = (n-1)*(spacingX+cellsizeX)
        _scrollRect.content.sizeDelta = new Vector2((totalPage - 1) * (spacingX + cellsizeX), content.rect.height);


        //根据总页数计算每页的NormalizedPosition数值并存储进数组
        float tempNormalizedPositon = 0;
        pagelNormalizedPositon = new float[totalPage];
        int length = pagelNormalizedPositon.Length;
        for (int i = 0; i < length; i++)
        {
            pagelNormalizedPositon[i] = tempNormalizedPositon;
            tempNormalizedPositon += 1f / (totalPage - 1);
        }
    }

    private void Update()
    {
        //缓动到目标页
        if (isDrag == false)
        {
            //Time秒到达目标的NormalizedPosition数值
            factor += Time.deltaTime / time;
            _scrollRect.horizontalNormalizedPosition = Mathf.Lerp(_scrollRect.horizontalNormalizedPosition, targetNormalizedPosition, factor);
        }
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        isDrag = true;
        beginMousePosX = Input.mousePosition.x;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        factor = 0;
        isDrag = false;
        endMousePosX = Input.mousePosition.x;
        float offsetX = beginMousePosX - endMousePosX;

        if (canMoveMult)
        {
            //单页移动
            if (Mathf.Abs(offsetX) >= moveOnePageDis && Mathf.Abs(offsetX) < moveMultPageDis)
            {
                //寻找距离当前拖拽结束位置最近的页
                float tempHorizontalNormailzed = _scrollRect.horizontalNormalizedPosition;
                float minOffset = Mathf.Abs(pagelNormalizedPositon[0] - tempHorizontalNormailzed);
                int length = pagelNormalizedPositon.Length;
                for (int i = 0; i < length; i++)
                {
                    float tempOffset = Mathf.Abs(pagelNormalizedPositon[i] - tempHorizontalNormailzed);
                    if (tempOffset <= minOffset)
                    {
                        minOffset = tempOffset;
                        pageIndex = i;
                    }
                }
            }
            //多页移动
            else if (Mathf.Abs(offsetX) >= moveMultPageDis)
            {
                if (offsetX > 0)
                {
                    pageIndex = totalPage - 1;
                }
                else if (offsetX < 0)
                {
                    pageIndex = 0;
                }
            }
            //滑动的距离过小
            else
            { }
        }
        else
        {
            //单页移动
            if (Mathf.Abs(offsetX) >= moveOnePageDis)
            {
                //寻找距离当前拖拽结束位置最近的页
                float tempHorizontalNormailzed = _scrollRect.horizontalNormalizedPosition;
                float minOffset = Mathf.Abs(pagelNormalizedPositon[0] - tempHorizontalNormailzed);
                int length = pagelNormalizedPositon.Length;
                for (int i = 0; i < length; i++)
                {
                    float tempOffset = Mathf.Abs(pagelNormalizedPositon[i] - tempHorizontalNormailzed);
                    if (tempOffset <= minOffset)
                    {
                        minOffset = tempOffset;
                        pageIndex = i;
                    }
                }
            } //滑动的距离过小
            else
            { }
        }

        targetNormalizedPosition = pagelNormalizedPositon[pageIndex];
    }
}

 

 

——通过改变ScrollView下Content的局部位置

using System;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.UI;

public class ExtensionScrollView : MonoBehaviour, IBeginDragHandler, IEndDragHandler
{
    private ScrollRect _scrollRect; //ScrollRect组件
    private RectTransform content; //ScrollRect组件的Content

    //==========ScrollRect的Content的一些参数
    private float cellsizeX; //格子的长度X
    private float spacingX; //间隔X
    private float onePageDis; //一页的距离

    private int totalPage; //总页数
    private int currentPage = 1; //当前页
    private float targetContentPos; //Content的目标位置X

    private float beginMousePosX; //鼠标开始按下的位置X
    private float endMousePosX; //鼠标结束按下的位置X
    private bool isDrag; //是否在拖拽

    [Header("缓动到目标页的时间")]
    public float time; //平滑时间

    private float factor; //比例因数

    [FormerlySerializedAs("moveOnePageDis")]
    [Header("移动一页的距离")]
    [Header("==========")]
    public float moveOnePageDis; //移动一页的距离

    [FormerlySerializedAs("moveMultPageDis")]
    [Header("移动多页的距离")]
    public float moveMultPageDis; //移动多页的距离

    private void Awake()
    {
        _scrollRect = GetComponent();
        content = _scrollRect.content;
        totalPage = content.transform.childCount;
        cellsizeX = content.GetComponent().cellSize.x;
        spacingX = content.GetComponent().spacing.x;
        onePageDis = cellsizeX + spacingX;

        //动态设置Content的大小:sizeDelta = (n-1)*(spacingX+cellsizeX)
        _scrollRect.content.sizeDelta = new Vector2((totalPage - 1) * (spacingX + cellsizeX), content.rect.height);
    }

    private void Update()
    {
        //缓动到目标页
        if (isDrag == false)
        {
            //Time秒到达targetContentPos的位置
            factor += Time.deltaTime / time;
            float tempX = Mathf.Lerp(content.localPosition.x, targetContentPos, factor);
            content.localPosition = new Vector3(tempX, content.localPosition.y);
        }
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        isDrag = true;
        beginMousePosX = Input.mousePosition.x;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        factor = 0;
        isDrag = false;
        endMousePosX = Input.mousePosition.x;
        float offsetX = beginMousePosX - endMousePosX;

        //单页移动
        if (Mathf.Abs(offsetX) >= moveOnePageDis && Mathf.Abs(offsetX) < moveMultPageDis)
        {
            if (offsetX > 0)
            {
                if (currentPage + 1 > totalPage)
                {
                    return;
                }

                currentPage += 1;
                targetContentPos -= onePageDis;
            }
            else if (offsetX < 0)
            {
                if (currentPage - 1 < 1)
                {
                    return;
                }

                currentPage -= 1;
                targetContentPos += onePageDis;
            }
        }
        //多页移动
        else if (Mathf.Abs(offsetX) >= moveMultPageDis)
        {
            if (offsetX > 0)
            {
                targetContentPos -= (totalPage - currentPage) * onePageDis;
                currentPage = totalPage;
            }
            else if (offsetX < 0)
            {
                targetContentPos += (totalPage - currentPage) * onePageDis;
                currentPage = 1;
            }
        }
        //滑动的距离过小
        else
        { }
    }
}

五:自己编写代码实现Scroll View

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class MyScrollView : MonoBehaviour {
    [Header("======弹性设置=======")]
    public bool canOver = false;        //是否可以超出内容边界
    public float overNum = 0.3f;          //超出多少
    public float overTime = 0.1f;       //弹回时间
    [Header("======滑动系数=======")]
    public float sliderXStrength = 0.001f;    //横向滑动系数
    public float sliderYStrength = 0.001f;    //纵向滑动系数
    [Header("======滑动方向=======")]
    public bool horizontal = true;      //横向滑动
    public bool vertical = true;        //纵向滑动
 
    private Transform content;       //内容
    private float minContentPosX;    //内容横向开始位置
    private float maxContentPosX;    //内容横向结束位置
    private float minContentPosY;    //内容纵向开始位置
    private float maxContentPosY;    //内容纵向结束位置
 
    private float mouseBeginPosX;    //鼠标横向开始位置
    private float mouseEndPosX;      //鼠标横向结束位置
    private float mouseBeginPosY;    //鼠标纵向开始位置
    private float mouseEndPosY;      //鼠标纵向结束位置
 
    private float valueX = 1.0f;
    private float valueY = 1.0f;
 
    private bool isDrag;         //判断鼠标是否在滑
    private float bounceFri;     //SmoothDamp系数
 
    private void Awake()
    {
        content = transform.Find("Content");
        if (horizontal) {
            minContentPosX = 540.0f;      //内容滑到最左边的位置
            maxContentPosX = -540.0f;     //内容滑倒最右边的位置
        }
        if (vertical) {
            minContentPosY = 213.0f;      //内容滑到最下边的位置
            maxContentPosY = -209.0f;     //内容滑到最上边的位置
        }
    }
 
    void Update () {
        //鼠标按下,开始滑动,获取鼠标位置
		if(Input.GetMouseButtonDown(0))
        {
            isDrag = true;
            if (horizontal) {
                mouseBeginPosX = Input.mousePosition.x;
                mouseEndPosX = Input.mousePosition.x;
            }
            if (vertical)
            {
                mouseBeginPosY = Input.mousePosition.y;
                mouseEndPosY = Input.mousePosition.y;
            }
            else {
                return;
            }
        }
        //鼠标抬起,滑动结束
        if(Input.GetMouseButtonUp(0))
        {
            isDrag = false;
        }
        //滑动过程
        if(isDrag)
        {
            if(Input.GetMouseButton(0))
            {
                //不断获取鼠标位置的差值,判断方向
                mouseBeginPosX = mouseEndPosX;
                mouseBeginPosY = mouseEndPosY;
                if (horizontal) {
                    mouseEndPosX = Input.mousePosition.x;
                    float offsetX = mouseEndPosX - mouseBeginPosX;
                    valueX += offsetX * sliderXStrength;
                    if (canOver)
                    {
                        valueX = Mathf.Max(0 - overNum, valueX);
                        valueX = Mathf.Min(valueX, 1 + overNum);
                    }
                    else
                    {
                        valueX = Mathf.Max(0f, valueX);
                        valueX = Mathf.Min(valueX, 1f);
                    }
                }
                if (vertical) {
                    mouseEndPosY = Input.mousePosition.y;
                    float offsetY = mouseEndPosY - mouseBeginPosY;
                    valueY += offsetY * sliderYStrength;
                    if (canOver)
                    {
                        valueY = Mathf.Max(0 - overNum, valueY);
                        valueY = Mathf.Min(valueY, 1 + overNum);
                    }
                    else
                    {
                        valueY = Mathf.Max(0f, valueY);
                        valueY = Mathf.Min(valueY, 1f);
                    }
                }
            }
        }
        //弹回来的强度
        if (horizontal) {
            if (valueX > 1f && isDrag == false)
            {
                valueX = Mathf.SmoothDamp(valueX, 1f, ref bounceFri, overTime);
            }
            if (valueX < 0f && isDrag == false)
            {
                valueX = Mathf.SmoothDamp(valueX, 0f, ref bounceFri, overTime);
            }
        }
        if (vertical) {
            if (valueY > 1f && isDrag == false)
            {
                valueY = Mathf.SmoothDamp(valueY, 1f, ref bounceFri, overTime);
            }
            if (valueY < 0f && isDrag == false)
            {
                valueY = Mathf.SmoothDamp(valueY, 0f, ref bounceFri, overTime);
            }
        }
        //把value值转换为pos值,赋值给内容的pos
        float posX = valueX * (maxContentPosX - minContentPosX) + minContentPosX;
        float posY = valueY * (maxContentPosY - minContentPosY) + minContentPosY;    
        content.localPosition = new Vector3(posX, posY, content.localPosition.z);
    }
}

 

你可能感兴趣的:(UGUI技术)