Unity中关于ScrollRect组件完整解决方案(ScrollRect中元素自动排版+ScrollRect中元素自动定位到Viewport可见范围内)

一、元素自动排版功能

1、首先要往我们的unity项目中导入两个脚本文件,脚本文件名称分别是UIScrollEventListener和CZScrollRect,这两个脚本文件代码如下所示。

1-1、介绍UIScrollEventListener脚本写法。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;public class UIScrollEventListener : MonoBehaviour, IBeginDragHandler, IEndDragHandler , IPointerUpHandler, IPointerDownHandler , IDragHandler
{public delegate void VoidDelegate(PointerEventData pdata);public VoidDelegate onBeginDrag;public VoidDelegate onEndDrag;public VoidDelegate onUp;public VoidDelegate onDown;public VoidDelegate onDrag;bool isDrag = false;List<Image> registers = new List<Image>();public static UIScrollEventListener Get(GameObject go){UIScrollEventListener listener = go.GetComponent<UIScrollEventListener>();if (listener == null) listener = go.AddComponent<UIScrollEventListener>();return listener;}public void OnBeginDrag(PointerEventData eventData){isDrag = true;if (onBeginDrag != null) onBeginDrag(eventData);}public void OnEndDrag(PointerEventData eventData){isDrag = false;if (onEndDrag != null) onEndDrag(eventData);}public void OnPointerUp(PointerEventData eventData){if (!isDrag) {SetRegisterEvent(true);PraseObject(eventData);SetRegisterEvent(false);}if (onUp != null) onUp(eventData);}public void OnPointerDown(PointerEventData eventData){if (onDown != null) onDown(eventData);}public void OnDrag(PointerEventData eventData) {if (onDrag != null) onDrag(eventData);}public void RegisterButton(GameObject go){Image img = go.GetComponent<Image>();if (img != null){img.raycastTarget = false;registers.Add(img);}}void SetRegisterEvent(bool b) {if (registers.Count > 0){for (int i = 0; i < registers.Count; ++i){registers[i].raycastTarget = b;}}}void PraseObject(PointerEventData eventData) {if (registers.Count > 0){for (int i = 0; i < registers.Count; ++i){if (EventSystem.current != null){List<RaycastResult> result = new List<RaycastResult>();EventSystem.current.RaycastAll(eventData, result);foreach (RaycastResult r in result){//Debug.Log(r.gameObject.name);foreach (Image img in registers){if (img.gameObject.Equals(r.gameObject)){InputField inputfield = img.gameObject.GetComponent<InputField>();if (inputfield != null) inputfield.ActivateInputField();}}}}}}}}
1-2、介绍CZScrollRect脚本写法。
using System;
using UnityEngine;
using UnityEngine.UI;public class ScrollObj
{public GameObject obj;public int dex;
}public class CZScrollRect
{public enum TipType{UNDO_REFRESH = 0,PULL_REFRESH = 1,UNDO_APPEND = 2,PULL_AAPEND = 3,NODATA = 4,NONE = 5}const int OPEAT_HEIGHT = 100;//高度差判断操作类型const int INIT_NUM_LIMIT = 8;//列表实例化个数public delegate void OperatDelegate();public delegate void OperatObjDelegate(GameObject obj , int index);public delegate void OperatTextObjDelegate(GameObject obj, TipType t);public OperatDelegate onRefresh;//下拉刷新时回调public OperatDelegate onAppend;//需要加载更多时回调public OperatObjDelegate onScrollObj;//需刷新时回调public OperatTextObjDelegate onUpdateTextObj;//需刷新文本状态时回调public ScrollRect scrollRect;//ScrollRectprivate RectTransform scrollRectContent;//RectTransformpublic GameObject prefab;//实例化的对象public GameObject text_up;//下拉刷新文本public GameObject text_down;//上拉加载更多文本TipType textup_status;int opeartLen = 0;//记录总长度public int layoutwidth = 1242;//填写item的长度public int limitNum = 8;//列表实例化个数public float interval = 200;//每个item的高度public float spacing = 5;//每个tiem的间隔ScrollObj[] list;//用于管理生成的对象int opeartType;int pageindex;//页码bool bHasMore;//是否能加载更多int halfWidth;//public GameObject batchContent;public CZScrollRect(){opeartType = -1;hasMore = false;}/// <summary>/// 用于控制scrollrect是否能够滑动/// (用于等待网络请求等业务)/// </summary>public bool vertical{get{return scrollRect.vertical;}set{scrollRect.vertical = value;}}/// <summary>/// 是否存在更多/// </summary>public bool hasMore{get{return bHasMore;}set{bHasMore = value;}}/// <summary>/// 获取对象所在的索引/// </summary>/// <param name="obj"></param>/// <returns></returns>public int GetObjIndex(GameObject obj) {for(int i = 0; i < list.Length; ++i){if (obj.Equals(list[i].obj)){return list[i].dex;}}return -1;}/// <summary>/// 初始化参数/// </summary>public void Init() {list = new ScrollObj[Mathf.Max(limitNum, INIT_NUM_LIMIT)];scrollRectContent = scrollRect.content;halfWidth = layoutwidth / 2;//此处监听drag事件UIScrollEventListener.Get(scrollRect.gameObject).onDrag = (data) =>{float recty = -scrollRectContent.rect.y - scrollRect.GetComponent<RectTransform>().sizeDelta.y;//Log.Debug($"{scrollRectContent.anchoredPosition.y} , {recty} , {-scrollRectContent.rect.y}");if (scrollRectContent.anchoredPosition.y >= recty + OPEAT_HEIGHT){if (bHasMore){//松开可以加载更多if (textup_status != TipType.UNDO_APPEND) {textup_status = TipType.UNDO_APPEND;if (onUpdateTextObj != null) onUpdateTextObj(text_down, TipType.UNDO_APPEND);}}else{//没有更多数据了if (textup_status != TipType.NODATA){textup_status = TipType.NODATA;if (onUpdateTextObj != null) onUpdateTextObj(text_down, TipType.NODATA);}}opeartType = 1;}else if (scrollRectContent.anchoredPosition.y > recty){if (bHasMore){//上拉可以加载更多if (textup_status != TipType.PULL_AAPEND){textup_status = TipType.PULL_AAPEND;if (onUpdateTextObj != null) onUpdateTextObj(text_down, TipType.PULL_AAPEND);}}else{//没有更多数据了if (textup_status != TipType.NODATA){textup_status = TipType.NODATA;if (onUpdateTextObj != null) onUpdateTextObj(text_down, TipType.NODATA);}}opeartType = -1;}else if (scrollRectContent.anchoredPosition.y <= -OPEAT_HEIGHT){//松开可以刷新if (textup_status != TipType.UNDO_REFRESH){textup_status = TipType.UNDO_REFRESH;if (onUpdateTextObj != null) onUpdateTextObj(text_up, TipType.UNDO_REFRESH);}opeartType = 0;}else if (scrollRectContent.anchoredPosition.y < 0){//下拉可以刷新if (textup_status != TipType.PULL_REFRESH){textup_status = TipType.PULL_REFRESH;if (onUpdateTextObj != null) onUpdateTextObj(text_up, TipType.PULL_REFRESH);}opeartType = -1;}else{opeartType = -1;}UpdateUiInfo();};//此处是监听鼠标点击事件  UIScrollEventListener.Get(scrollRect.gameObject).onUp = (data) =>{if (opeartType == 0){if (onRefresh != null){scrollRect.vertical = false;onRefresh();}}else if (opeartType == 1){if (bHasMore && onRefresh != null){scrollRect.vertical = false;onAppend();}}};scrollRect.onValueChanged.RemoveAllListeners();scrollRect.onValueChanged.AddListener(OnScrollChange);}void OnScrollChange(Vector2 v) {//TOP TO BUTTOM  计算页码int curIndex = Mathf.Min(Mathf.Max(Mathf.FloorToInt(scrollRectContent.anchoredPosition.y / (interval + spacing)) - 1, 0), opeartLen - limitNum);UpdatePageIndex(curIndex);}/// <summary>/// 刷新页面 (重置长度)/// </summary>/// <param name="len"></param>public void Refresh(int len){//Debug.Log($"Refresh len = {len}");if (len < 0)return;int count = 0;for (int i = 0; i < list.Length; ++i) {if (list[i] == null) list[i] = new ScrollObj();if (i < len) {if (list[i].obj == null){list[i].obj = GetNewObject();}list[i].dex = i;list[i].obj.SetActive(true);SetPosition(list[i].obj, list[i].dex);if (onScrollObj != null) onScrollObj(list[i].obj, list[i].dex);count++;}else{if(list[i].obj != null) list[i].obj.SetActive(false);}}opeartLen = len;UpdatePageIndex(0);//重置页码scrollRectContent.localPosition = Vector3.zero;scrollRect.verticalScrollbar.value = 1;UpdateUiInfo();scrollRect.vertical = true;}/// <summary>/// 追加长度/// </summary>/// <param name="len"></param>public void Append(int len){//Debug.Log($"Append len = {len}");if (len < 0) return;if(len == 0){if (onUpdateTextObj != null) {onUpdateTextObj(text_up, TipType.NONE);onUpdateTextObj(text_down, TipType.NONE);}}if (opeartLen < list.Length) {int showlen = Mathf.Min(list.Length - opeartLen , len);//Debug.Log($"showlen = {showlen}");for (int i = 0; i < showlen; ++i) {int dex = opeartLen + i;//Debug.Log(dex);if (list[dex] == null) list[dex] = new ScrollObj();if (list[dex].obj == null){list[dex].obj = GetNewObject();}list[dex].dex = dex;list[dex].obj.SetActive(true);SetPosition(list[i].obj, list[i].dex);if (onScrollObj != null) onScrollObj(list[dex].obj, list[dex].dex);}}opeartLen += len;UpdateUiInfo();scrollRect.vertical = true;}/// <summary>/// 实时刷新页面/// </summary>/// <param name="pdex"></param>void UpdatePageIndex(int pdex) {if (opeartLen <= list.Length || pageindex == pdex) return;//Debug.Log($"pdex = {pdex}");int x = Mathf.FloorToInt(pdex / limitNum);int y = pdex % limitNum;for (int i = 0; i < limitNum; ++i){int d = 0;if (i < y){d = (x + 1) * limitNum + i;}else{d = Mathf.Max(x, 0) * limitNum + i;}if (list[i].dex != d) {list[i].dex = d;if (list[i].obj != null) {SetPosition(list[i].obj, list[i].dex);if (onScrollObj != null) onScrollObj(list[i].obj, list[i].dex);}}}pageindex = pdex;}/// <summary>/// 刷新content的高度/// </summary>void UpdateUiInfo() {//Debug.Log($"opeartLen = {opeartLen} {opeartLen * (interval + spacing)} - {scrollRect.GetComponent<RectTransform>().sizeDelta.y}");scrollRectContent.sizeDelta = new Vector2(0, Math.Max(opeartLen * (interval + spacing), scrollRect.GetComponent<RectTransform>().sizeDelta.y));if(text_up != null) text_up.transform.localPosition = new Vector3(text_up.transform.localPosition.x, OPEAT_HEIGHT - 50, 0);if (text_down != null) text_down.transform.localPosition = new Vector3(text_down.transform.localPosition.x, -scrollRectContent.sizeDelta.y - OPEAT_HEIGHT + 50, 0);}void SetPosition(GameObject obj , int dex) {//obj.transform.localPosition = new Vector3(0, -dex * (interval + spacing) - interval / 2, 0);float y = -dex * (interval + spacing) - interval;obj.transform.GetComponent<RectTransform>().offsetMin = new Vector2(0 , y);obj.transform.GetComponent<RectTransform>().offsetMax = new Vector2(0, y + interval);}GameObject GetNewObject() {return GameObject.Instantiate(prefab, scrollRectContent);}public void Dispose(){scrollRect.onValueChanged.RemoveAllListeners();scrollRect = null;scrollRectContent = null;prefab = null;text_up = null;text_down = null;list = null;onAppend = null;onRefresh = null;onScrollObj = null;}
}

二、测试ScrollRect中元素自动排版案例

1、首先要在我们的场景中搭建好测试的游戏物体,我这个场景中游戏物体的锚点是随便设置的,你们可以根据需要进行对应不同的测试。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2、新增一个TestScrollView脚本用于测试ScrollRect中元素自动排版,TestScrollView脚本完整代码如下。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class TestScrollView : MonoBehaviour
{CZScrollRect scrollcomponent;GameObject item;ScrollRect m_ScrollRect;List<string> str_list = new List<string>{"元素1","元素2","元素3","元素4","元素5","元素6","元素7","元素8","元素9","元素10","元素11","元素12"};private void Awake(){item = transform.Find("Viewport/Item").gameObject;m_ScrollRect = gameObject.GetComponent<ScrollRect>();scrollcomponent = new CZScrollRect();scrollcomponent.prefab = item;scrollcomponent.scrollRect = m_ScrollRect;scrollcomponent.onScrollObj = OnScrollObj;scrollcomponent.interval = 30;scrollcomponent.limitNum = 10000;scrollcomponent.Init();}private void Start(){if (str_list.Count == 0){scrollcomponent.Refresh(0);}else{scrollcomponent.Refresh(str_list.Count);}}void OnScrollObj(GameObject obj, int index){Debug.Log("obj name:" + obj.name + "       index:" + index);obj.transform.Find("Text").GetComponent<Text>().text = str_list[index];}
}

3、ScrollRect中元素自动排版效果展示。

在这里插入图片描述
在这里插入图片描述

二、ScrollRect中元素自动定位到Viewport可见范围内

1、在做之前,需要在项目中导入DOTween插件。

在这里插入图片描述

2、首先要往我们的unity项目中导入一个脚本文件,脚本文件名称是UIScrollRect,这个脚本文件代码如下所示。

using DG.Tweening;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Options;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;public class UIScrollRect : ScrollRect
{[SerializeField]private UnityEvent _BeginDragEvent;[SerializeField]private UnityEvent _DragEvent;[SerializeField]private UnityEvent _EndDragEvent;public bool LeftOrUpMove { get; private set; }private Vector3 _BeginDragPosition;private bool _IsDragEnd = false;private bool _UseMoveCenter = false;public bool UseMoveCenter { set { _UseMoveCenter = value; } }private bool _AutoMoveCenter = false;private bool AutoMoveCenter { set { _AutoMoveCenter = value; } }private bool _SkipAnimation = false;private bool _SkipDragDirection = false;private Vector3 _ContentPosition = Vector3.zero;private TweenerCore<Vector3, Vector3, VectorOptions> _DOTweenTo;public UnityEvent BeginDragEvent{set { this._BeginDragEvent = value; }get { return this.BeginDragEvent; }}public UnityEvent DragEvent{set { this._DragEvent = value; }get { return this.DragEvent; }}public UnityEvent EndDragEvent{set { this.EndDragEvent = value; }get { return this._EndDragEvent; }}public override void OnBeginDrag(PointerEventData point){base.OnBeginDrag(point);if (this._DOTweenTo != null)this._DOTweenTo.Pause();this._BeginDragPosition = this.content.localPosition;this._BeginDragEvent?.Invoke();}public override void OnDrag(PointerEventData point){base.OnDrag(point);if (this._DOTweenTo != null){this._DOTweenTo.Pause();this._DOTweenTo = null;}this._DragEvent?.Invoke();}public override void OnEndDrag(PointerEventData point){base.OnEndDrag(point);this._IsDragEnd = true;this._ContentPosition = this.content.localPosition;this.LeftOrUpMove = this.horizontal ? this._BeginDragPosition.x > this._ContentPosition.x : this._BeginDragPosition.y < this._ContentPosition.y;this._EndDragEvent?.Invoke();}protected override void LateUpdate(){base.LateUpdate();if (this._UseMoveCenter && this._IsDragEnd){Vector3 pos = this.content.localPosition;Vector3 offetset = pos - this._ContentPosition;if (Mathf.Abs(this.horizontal ? offetset.x : offetset.y) < 0.2f){this._IsDragEnd = false;if (this._AutoMoveCenter)this.OnItemMoveCenter(null, this._SkipAnimation, this._SkipDragDirection);}this._ContentPosition = pos;}}/// <summary>///	设置中心滑动参数/// </summmary>/// <param name="auto">滑动结束后自动调用OnItemMoveCenter</param>/// <param name="skipAnimation">跳过动画直接设置中心位置</param>/// <parm name"skipDragDirection">忽略滑动方向,查找距离中心位置最近的item滑动</param>public void UseMoveMoveCenter(bool auto = true, bool skipAnimation = false, bool skipDragDirection = false){this._UseMoveCenter = true;this._AutoMoveCenter = auto;this._SkipAnimation = skipAnimation;this._SkipDragDirection = skipDragDirection;_ContentPosition = Vector3.zero;}/// <summary>/// 将target滑动到中文位置,为null时选择距离中心点最近的item滑动/// </summary>/// <param name="target">目标item</param>/// <param name="skipAnimation">跳过动画直接设置中心位置</param>/// <param name="skipDragDirection">忽略滑动方向,查找距离中心位置最近的item滑动</param>public void OnItemMoveCenter(Transform target = null, bool skipAnimation = false, bool skipDragDirection = false){int minIndex = 0;float distance = float.MaxValue;int count = this.content.childCount;if (count == 0)return;RectTransform scrollRectTrans = this.GetComponent<RectTransform>();Vector3 pos = scrollRectTrans.localPosition;//目标存在直接计算到中心位置的距离if (target != null){RectTransform item = target.GetComponent<RectTransform>();// 使用Transform转换方法InverseTransformPoint,将item的世界坐标转化为Transform的局部坐标pos = scrollRectTrans.InverseTransformPoint(item.position);// 需要注意的事是item的锚点不同带来的偏差,这里锚点的标准值为(0.5f, 0.5f)即锚点在中心位置, 若不是标准值就要去除width和heigth的值Vector2 pivot = item.pivot;//滑动方向优先计算水平滑动,verticaldistance = this.horizontal ? (0.5f - pivot.x) * item.rect.width - pos.x : (pivot.y - 0.5f) * item.rect.height - pos.y;}else{// 遍历计算,距离中心点最近的距离以及对应的索引值for (int i = 0; i < count; i++){RectTransform item = this.content.GetComponent<RectTransform>();// 使用Transform转换方法InverseTransformPoint,将item的世界坐标转化为Transform的局部坐标pos = scrollRectTrans.InverseTransformPoint(item.position);// 需要注意的事是item的锚点不同带来的偏差,这里锚点的标准值为(0.5f, 0.5f)即锚点在中心位置, 若不是标准值就要去除width和heigth的值Vector2 pivot = item.pivot;//滑动方向优先计算水平滑动,verticalfloat dis = this.horizontal ? (0.5f - pivot.x) * item.rect.width - pos.x : (pivot.y - 0.5f) * item.rect.height - pos.y;if (Mathf.Abs(distance) > Mathf.Abs(dis)){distance = dis;minIndex = i;}}}bool dragDirection = !skipDragDirection && target == null;distance = this.horizontal ? this.CalculateHorizontalDistance(distance, minIndex, count, dragDirection) : this.CalculateVerticalDistance(distance, minIndex, count, dragDirection);pos = this.content.localPosition;if (this.horizontal)pos.x = distance;elsepos.y = distance;// 跳过动画,直接设置坐标if (skipAnimation){this.content.localPosition = pos;return;}//使用dotween动画移动, elasticity滑到头回弹的时间_DOTweenTo = (TweenerCore<Vector3, Vector3, VectorOptions>)this.content.DOLocalMove(pos, this.elasticity).OnComplete(() =>{this._DOTweenTo = null;});}/// <summary>/// 计算水平方向到中心点位置距离/// </summary>private float CalculateHorizontalDistance(float distance, int minIndex, int count, bool dragDirection){RectTransform rect = this.GetComponent<RectTransform>();//防止回弹计算//当item为null时,查找距离中心点最近的item,并滑动到中心点,当鼠标拖拽滑动结束时,自动移动到中心点的方向可能与鼠标拖拽方向相反,因此需要计算,不要计算时将_SkipDragDirection 设置为trueif (dragDirection){int index = minIndex;float dis = distance;//使用while,是因为可能存在多列,相同列上的距离是一样的,需要多次查找拖拽方向上的itemwhile (index < count && index >= 0 && dis == distance){if (this.LeftOrUpMove){if (distance > 0)index++;}else if (distance < 0)index--;if (index == minIndex)break;RectTransform item = this.content.GetChild(index).GetComponent<RectTransform>();Vector2 pivot = item.pivot;Vector3 pos = rect.InverseTransformPoint(item.position);distance = (pivot.x - 0.5f) * item.rect.width - pos.x;}distance += this.content.localPosition.x;if (this.movementType != ScrollRect.MovementType.Unrestricted){if (distance >= 0)distance = 0;else{float dragMaxDistance = rect.rect.width - this.content.rect.width;if (distance < dragMaxDistance)distance = dragMaxDistance;}}}return distance;}/// <summary>/// 计算水平方向到中心点位置距离/// </summary>private float CalculateVerticalDistance(float distance, int minIndex, int count, bool dragDirection){RectTransform rect = this.GetComponent<RectTransform>();//防止回弹计算//当item为null时,查找距离中心点最近的item,并滑动到中心点,当鼠标拖拽滑动结束时,自动移动到中心点的方向可能与鼠标拖拽方向相反,因此需要计算,不要计算时将_SkipDragDirection 设置为trueif (dragDirection){float dis = distance;int index = minIndex;//使用while,是因为可能存在多行,相同列上的距离是一样的,需要多次查找拖拽方向上的itemwhile (index < count && index >= 0 && dis == distance){if (this.LeftOrUpMove){if (distance < 0)index++;}else if (distance > 0)index--;if (index == minIndex)break;RectTransform item = this.content.GetChild(index).GetComponent<RectTransform>();Vector2 pivot = item.pivot;Vector3 pos = rect.InverseTransformPoint(item.position);distance = (pivot.y - 0.5f) * item.rect.height - pos.y;}}distance += this.content.localPosition.y;//Unrestricted滑动到头没有回弹,有回弹的无法将最后或者最前的item移动到中心点位置if (this.movementType != ScrollRect.MovementType.Unrestricted){if (distance <= 0)distance = 0;else{float dragMaxDistance = this.content.rect.height - rect.rect.height;if (distance > dragMaxDistance)distance = dragMaxDistance;}}return distance;}
}

3、修改游戏场景中名为Scroll View游戏物体身上的ScrollRect组件,修改方式就是把ScrollRect组件替换成上述的UIScrollRect。

3-1、没修改前Scroll View游戏物体身上的ScrollRect组件效果如下所示。

在这里插入图片描述

3-2、修改后Scroll View游戏物体身上的ScrollRect组件效果如下所示。

在这里插入图片描述

4、修改TestScrollView脚本测试ScrollRect中元素自动定位到Viewport可见范围内是否可行,TestScrollView脚本完整代码如下。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class TestScrollView : MonoBehaviour
{CZScrollRect scrollcomponent;GameObject item;ScrollRect m_ScrollRect;UIScrollRect m_UIScrollRect;GameObject m_targetGo;   //需要定位到的元素物体List<string> str_list = new List<string>{"元素1","元素2","元素3","元素4","元素5","元素6","元素7","元素8","元素9","元素10","元素11","元素12","元素13","元素14","元素15","元素16","元素17","元素18","元素19","元素20"};private void Awake(){item = transform.Find("Viewport/Item").gameObject;m_ScrollRect = gameObject.GetComponent<ScrollRect>();m_UIScrollRect = gameObject.GetComponent<UIScrollRect>();scrollcomponent = new CZScrollRect();scrollcomponent.prefab = item;scrollcomponent.scrollRect = m_ScrollRect;scrollcomponent.onScrollObj = OnScrollObj;scrollcomponent.interval = 30;scrollcomponent.limitNum = 10000;scrollcomponent.Init();}private void Start(){if (str_list.Count == 0){scrollcomponent.Refresh(0);}else{scrollcomponent.Refresh(str_list.Count);}Debug.Log("m_targetGo:" + m_targetGo);m_UIScrollRect.OnItemMoveCenter(m_targetGo.GetComponent<RectTransform>());}void OnScrollObj(GameObject obj, int index){//假设我们想要让元素10在Viewport可见范围内居中显示if (index == 9){m_targetGo = obj;obj.transform.Find("Text").GetComponent<Text>().text = "定位元素" + str_list[index];}else{obj.transform.Find("Text").GetComponent<Text>().text = str_list[index];}Debug.Log("obj name:" + obj.name + "       index:" + index);}
}

5、ScrollRect中元素自动定位到Viewport可见范围内效果展示,正是我们想要的让第10个元素在Viewport可见范围内居中显示。

在这里插入图片描述

三、注意事项

1、注意以下图中Scrollbar组件红框中元素的设置,最好和我的设置一样。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/687050.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu 20.04 安装RVM

RVM是管理Ruby版本的工具,使用RVM可以在单机上方便地管理多个Ruby版本。 下载安装脚本 首先使下载安装脚本 wget https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer 如果出现了 Connection refused 的情况, 可以考虑执行以下命令修改dns,再执…

【自然语言处理】seq2seq模型—机器翻译

清华大学驭风计划课程链接 学堂在线 - 精品在线课程学习平台 (xuetangx.com) 代码和报告均为本人自己实现&#xff08;实验满分&#xff09;&#xff0c;只展示主要任务实验结果&#xff0c;如果需要详细的实验报告或者代码可以私聊博主 有任何疑问或者问题&#xff0c;也欢…

Linux网络编程——守护进程

文章目录 1. 前台 & 后台进程2. Linux进程之间的关系3. 守护进程 本章Gitee仓库&#xff1a;守护进程 1. 前台 & 后台进程 在Linux系统登陆的时候&#xff0c;它会给我们形成一次会话&#xff0c;会话在系统里面会创建一个bash进程&#xff0c;这个bash就会给用户提供…

Java 抽象容器类源码剖析

总体介绍 抽象容器类接口和具体容器类的关系如图所示&#xff0c;顶层包括Collection、List、Set、Queue、Deque和Map6个抽象容器类。 AbstractCollection&#xff1a;实现了Collection接口&#xff0c;被抽象类AbstractList、AbstractSet、AbstractQueue继承&#xff0c;Arra…

7.1 Qt 中输入行与按钮

目录 前言&#xff1a; 技能&#xff1a; 内容&#xff1a; 参考&#xff1a; 前言&#xff1a; line edit 与pushbotton的一点联动 当输入行有内容时&#xff0c;按钮才能使用&#xff0c;并能读出输入行的内容 技能&#xff1a; pushButton->setEnabled(false) 按钮不…

166基于matlab的通过峭度指标与互相关系数筛选IMF进行SVD分解去噪

基于matlab的通过峭度指标与互相关系数筛选IMF进行SVD分解去噪&#xff0c;分辨虚假imf&#xff0c;提取最大峭度imf图。输出去噪前后时域及其包络谱结果。程序已调通&#xff0c;可直接运行。 166 matlab SVD去噪 IMF筛选 包络谱 (xiaohongshu.com)

Android安卓架构MVC、MVP、MVVM模式的概念与区别

目录 MVC框架 MVP框架 MVVM框架 MVVM与MVP区别 MVVM与MVC区别 MVC、MVP、MVVM模式哪个要好一些 MVC&#xff08;Model-View-Controller&#xff09;、MVP&#xff08;Model-View-Presenter&#xff09;、MVVM&#xff08;Model-View-ViewModel&#xff09;是三种常见的软…

Json格式文件

1.把Java对象转换成Json格式 1.1.导入依赖 这里推荐一个插件Jackson&#xff0c;其提供的类可以让Java的类转换成Jason格式文件 <dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><vers…

Camtasia2023破解版下载

Camtasia2023破解版是一款简单好用的屏幕录像软件&#xff0c;它结屏幕录制和录制视频编辑等功能于一身&#xff0c;不管是用来创建视频教程还是相关教学视频或者操作演示等都是不二的选择&#xff0c;软件支持录制全屏或者部分区域进行选择录制&#xff0c;可录制网络摄像头、…

安卓实现简单砸地鼠游戏

效果 布局 <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.android.com/apk/res-auto"xmlns:tools"http://schemas.android.com/tools"android:layout_width"match_parent"a…

Muse专业版教程:制作简谱,制作吉他谱

UP教你制作吉他谱,muse专业版吉他谱制作过程分享_哔哩哔哩_bilibili教学讲解-小宁视频制作-狂奔的琴弦软件-Muse专业版后面会分集录从零开始制作吉他谱,感兴趣的小伙伴点一波关注, 视频播放量 15457、弹幕量 1、点赞数 208、投硬币枚数 127、收藏人数 424、转发人数 59, 视频…

DNS服务正反解析

1.正向解析 1.配置基本 1.1防火墙配置 二者都要关闭 setenforce 0 systemctl stop firewalld #关闭防火墙 yum install bind -y #下载bind软件 客户端可以不用下 1.2服务端配置静态ip&#xff0c; ip a 查看网卡 nmcli c modify ens33 ipv4.method manual ipv4.addresses …

OpenAI发布Sora模型,可根据文字生成逼真AI视频

早在2022年11月30日&#xff0c;OpenAI第一次发布人工智能聊天机器人ChatGPT&#xff0c;随后在全世界掀起了人工智能狂潮&#xff0c;颠覆了一个又一个行业。在过去的一年多的时间里&#xff0c;chatGPT的强大功能改变了越来越多人的工作和生活方式&#xff0c;成为了世界上用…

Python教程(26)——Python迭代器和生成器详解

迭代器 Python中的迭代器是一种对象&#xff0c;它可以迭代&#xff08;遍历&#xff09;一个可迭代对象&#xff08;比如列表、元组或字符串&#xff09;的元素。迭代器用于实现迭代器协议&#xff0c;即包含 __iter__() 方法和 __next__() 方法。 迭代器的工作原理是每次调…

数据的力量:构筑现代大型网站之数据库基础与应用

目录 数据库基础知识--前言 大型网站架构特点 DBA数据库管理员 什么是数据? 数据存储 什么是数据库 数据表的概念 为什么需要mysql这样的数据库管理工具&#xff1f;★ DBMS 收费数据库与免费数据库 运维和数据库 开发与运维的不同阶段 数据库类别 数据库具体应用…

【机器学习笔记】11 支持向量机

支 持 向 量 机 &#xff08; Support Vector Machine,SVM &#xff09; 支 持 向 量 机 是 一 类 按 监 督 学 习 &#xff08; supervisedlearning&#xff09;方式对数据进行二元分类的广义线性分类器&#xff08;generalized linear classifier&#xff09;&#xff0c;其…

C#安装CommunityToolkit.Mvvm依赖

这里需要有一定C#基础&#xff0c; 首先找到右边的解决方案&#xff0c;右键依赖项 然后选择nuget管理 这里给大家扩展一下nuget的国内源&#xff08;https://nuget.cdn.azure.cn/v3/index.json&#xff09; 然后搜自己想要的依赖性&#xff0c;比如CommunityToolkit.Mvvm 再点…

Linux超详细笔记

文章目录 Linux学习笔记操作系统Linux初识Linux的诞生Linux内核Linux发行版 虚拟机VMware安装远程连接Linux系统FinalShellFinalShell连接Linux WSL配置UbuntuLinux常用命令1.入门2.ls命令cd命令3.pwd命令4.相对路径和绝对路径5.mkdir命令6.文件操作命令&#xff08;1&#xff…

vue打包优化,webpack的8大配置方案

vue-cli 生成的项目通常集成Webpack &#xff0c;在打包的时候&#xff0c;需要webpack来做一些事情。这里我们希望它可以压缩代码体积&#xff0c;提高运行效率。 文章目录 &#xff08;1&#xff09;代码压缩&#xff1a;&#xff08;2&#xff09;图片压缩&#xff1a;&…

17.3.2.9 像素处理与内存处理之比较

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 通过第17.3.2.1节到第17.3.2.8节&#xff0c;相信读者对通过锁定内存来处理图像有了一定认识。与第17.3.1节相比较&#xff0c;可以…