Dropdown(下拉菜单)
核心
显示当前选项的文本框
下拉列表的模板(Template)
下拉列表中选项的模板(Item),Item上必须有Toggle组件
运行原理
当点击下拉菜单时,通过Template创建一个下拉列表
通过选项的模板(Item)创建每一个选项
属性
Template
下拉列表的模板
Caption Text
指定当前选项的文本框
Caption Image
指定当前选项的图片
Item Text
下拉列表中选项的模板(Item)中的文本框
Item Image
下拉列表中选项的模板(Item)中的图片
解释:选择哪一个Item,就将该Item中文本框的内容显示在Caption Text,将Item中的图片显示Caption Image
Options
下拉选项(文字和图片)
Value
当前选择是Options第几个选项(索引)
OnValueChanged
当索引Value值发生改变时触发OnValueChanged中的方法
代码添加的方式只能添加无返回值,有一个Int类型参数的方法
Canvas Group
控制当前游戏物体下的所有子物体的相关属性
属性
Alpha:透明度
Interactable:控制当前物体下所有子物体是否可以交互
Blocks Raycast:控制当前物体所有的子物体是否可以接受射线检测(UGUI事件系统的射线)
Ignore Parent Groups:是否忽略父物体中Canvas Group的影响,勾选是忽略,不能再Canvas里测试,要在子物体中才有效
UGUI的事件接口
命名空间
UnityEngine.EventSystems
接口
IPointerDownHandler
当鼠标在当前游戏物体的矩形框或其子物体的矩形框中按下时,触发该接口的方法
IPointerUpHandler
当鼠标在当前游戏物体的矩形框或其子物体的矩形框中抬起时,触发该接口的方法
IPointerEnterHandler
当鼠标进入到当前游戏物体或其子物体的矩形框时,触发该接口的方法
IPointerExitHandler
当鼠标移出到当前游戏物体或其子物体的矩形框时,触发该接口的方法
IPointerClickHandler
当鼠标点击当前游戏物体或其子物体时,触发该接口的方法
点击:按下跟抬起必须都在矩形框中才是点击,可以在父物体按下,子物体抬起
IDragHandler
拖动时(鼠标在当前游戏物体的矩形框按下后移动鼠标表示拖动)触发该方法
只要拖动就反复执行
IBeginDragHandler
开始拖动的一瞬间执行一次
IEndDragHandler
结束拖动的一瞬间执行一次
鼠标之前处于拖动情况,抬起的瞬间表示拖动结束
PinterEventData
button
判断左键,中键,右键
clickCount
点击次数
可以用于双击效果
enterEventCamera
进入矩形框时的事件相机
pressEventCamera
按下矩形框时的事件相机
pointerCurrentRaycast
鼠标当前射线检测的结果
gameObject:当前射线检测的游戏物体
pointerPressRaycast
鼠标按下时射线检测的结果
gameObject:鼠标按下时检测的游戏物体
position
当前鼠标在屏幕中的坐标
pressPosition
当前鼠标按下时在屏幕中的坐标
RectTransform坐标
position
世界坐标
当前游戏物体的轴心点相对于世界原点的坐标
localPosition
相对于父物体的坐标
当前游戏物体的轴心点相对于父物体的轴心点的坐标
anchoredPosition
锚点坐标
当前游戏物体的轴心点相对于锚点的坐标
右键点击事件
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; using UnityEngine.EventSystems; using System; public class RightButton : Selectable, IPointerClickHandler { //当点击按钮时,触发onClick中存储的方法 public ButtonClickedEvent onClick; public void OnPointerClick(PointerEventData eventData) { //当鼠标右键点击时,触发onClick的方法 if (eventData.button == PointerEventData.InputButton.Right) { onClick.Invoke(); } } [System.Serializable]//特性,修饰的是类ButtonClickedEvent //表示该ButtonClickedEvent类对象是可以序列化的 public class ButtonClickedEvent : UnityEvent { public ButtonClickedEvent() { } } }
PlayView.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.U2D; public class PlayerView : MonoBehaviour { public int rank; public Text rankText; public Text nameText; public Text levelText; public Text scoreText; public Image rankImage; /// <summary> /// 设置数据,修改显示内容 /// </summary> public void SetPlayerData(int rank, RankModel model) { this.rank = rank; nameText.text = model.name; levelText.text = "LV " + model.level; scoreText.text = string.Format("{0:N0}", model.maxScore);//model.maxScore.ToString(); SpriteAtlas atlas = Resources.Load<SpriteAtlas>("UITexture/Ranking"); //排名显示 //1,2,3名使用不同的图片, 显示的文本框隐藏 rank++; if (rank >= 1 && rank <= 3) { //从图集中加载精灵图 string spriteName = "icon_rank_" + rank; Sprite sprite = atlas.GetSprite(spriteName); rankImage.sprite = sprite; rankText.gameObject.SetActive(false); } else { rankText.gameObject.SetActive(true); rankText.text = rank.ToString(); rankImage.sprite = atlas.GetSprite("icon_rank_default"); } } }
RankPanel
using System.Collections; using System.Collections.Generic; using UnityEngine; public class RankPanel : MonoBehaviour { //测试 public int rankCount = 10; public int contentMaxChild = 6; public int playerTemplateHeight = 195; List<RankModel> ranks = new List<RankModel>(); private GameObject playerTemplate; private RectTransform cantent; //存储第一个子物体与最后一个子物体的 private PlayerView firstChild; private PlayerView lastChild; private void Awake() { InitTempData(); playerTemplate = transform.Find("Window/PlayerTemplate").gameObject; RectTransform ptrt = playerTemplate.transform as RectTransform; //先修改模板的高度 ptrt.sizeDelta = new Vector2(ptrt.sizeDelta.x, playerTemplateHeight); cantent = transform.Find("Window/PlayerRect/Viewport/Content") as RectTransform; //修改Content的高度, 高度 = 数据个数 * playerTemplateHeight cantent.sizeDelta = new Vector2(cantent.sizeDelta.x, playerTemplateHeight * ranks.Count); } private void Start() { InitPlayerView(); } private void Update() { CheckChildSwitch(); } /// <summary> /// 初始化临时数据 /// </summary> void InitTempData() { //自动生成10条数据 for (int i = 0; i < rankCount; i++) { RankModel rm = new RankModel(); rm.name = ""; for (int j = 0; j < Random.Range(5, 21); j++) { rm.name += (char)Random.Range('A', 'Z'); } rm.level = Random.Range(1, 100); rm.maxScore = Random.Range(0, 10000000); ranks.Add(rm); } //List的排序 //排序是根据分数进行排序 //Sort(Comparison<T> comparison) //public delegate int Comparison<T>(T x, T y); //Sort方法的参数是一个委托类型, 证明可以将一个方法作为参数传递进去 //但是方法必须与 Comparison 委托类型返回值与参数保持一致 // Comparison 返回值是int, 参数有两个 分别是 T 类型, T是泛型 /* public class List<T> { Sort(Comparison<T> comparison) } List<RankModel> ranks = new List<RankModel>(); * */ //对于ranks来说 T是RankModel //那么Comparison<T> T就是RankModel //总结,该Sort方法中参数可以传入一个 有int返回值, 两个参数,并且两个参数是RankModel类型 ranks.Sort( (RankModel m1, RankModel m2) => { //判断分数 if (m1.maxScore > m2.maxScore) { return -1; } else if (m1.maxScore < m2.maxScore) { return 1; } return 0; } ); } /// <summary> /// 初始化生成每一个玩家 /// </summary> void InitPlayerView() { int childCount = 0; if (ranks.Count < contentMaxChild) { //当排行榜的数据小于6个时,就不需要生成6个了 childCount = ranks.Count; } else { //最多生成6个 childCount = contentMaxChild; } for (int i = 0; i < childCount; i++) { GameObject player = Instantiate(playerTemplate, cantent); player.SetActive(true); player.name = "P_" + i.ToString(); //修改RectTransform的Left和Right为0 RectTransform rt = player.transform as RectTransform; //offsetMax -x = right -y = top //offsetMin x = left y = bottom rt.offsetMax = new Vector2(0, rt.offsetMax.y); rt.offsetMin = new Vector2(0, rt.offsetMin.y); //修改每一个的ancharedPosition的 Y //第0个就是0 //第1个就是一个的高度 rt.anchoredPosition = new Vector2(rt.anchoredPosition.x, -playerTemplateHeight * i); //告诉当前玩家显示区的数据是什么,排序是多少 player.GetComponent<PlayerView>().SetPlayerData(i, ranks[i]); } if (cantent.childCount > 0) { firstChild = cantent.GetChild(0).GetComponent<PlayerView>(); lastChild = cantent.GetChild(cantent.childCount - 1).GetComponent<PlayerView>(); } } /// <summary> /// 检测子物体是否需要交换 /// </summary> void CheckChildSwitch() { //当前数据数量超出 6条时才需要判断是否交换 if (ranks.Count < contentMaxChild) { return; } //通过Content的Y坐标判断首个子物体是否需要移动到最后 //Content.Y = 195 * 0 显示的Rank 0 - 5 //Content.Y = 195 * 1 显示的Rank 1 - 6 //Content.Y = 195 * 2 显示的Rank 2 - 7 //通过Content.Y能计算出 当前第一个子物体应该显示第几个人 // Content.Y / 195 = 第一个子物体的Rank // 195 就是 playerTemplateHeight //总结: 当前第一个子物体的Rank = 1 , 通过Content.Y计算出的第一个子物体的Rank = 2 //则 当前第一个子物体应该移动最后一个 int showFirstRank = (int)cantent.anchoredPosition.y / playerTemplateHeight; showFirstRank = Mathf.Clamp(showFirstRank, 0, ranks.Count - contentMaxChild); //Debug.Log("当前第一个应该显示:" + showFirstRank); //当前子物体 实际显示 //P_1, P_2, P_3, P_4, P_5, P_6 //通过坐标计算 应该显示的第一个的showFirstRank = 0, 最后一个lastRank = 5 //应该显示 P_0 ~ P_5 //该种情况, 就是最后一个需要移动到第一位置 //通过坐标计算 应该显示的第一个的showFirstRank = 2, 最后一个lastRank = 7 //应该显示 P_2 ~ P_7 //该种情况, 就是第一个需要移动到最后一个位置 //如果第一个子物体的Rank不是要显示的showRank, 就要交换 if (firstChild.rank < showFirstRank) { SwitchFirstToLast(); } else if (firstChild.rank > showFirstRank) { //最后一个移动到第一个 SwitchLastToFirst(); } } /// <summary> /// 交换第一个子物体到最后 /// </summary> void SwitchFirstToLast() { //当最后一个索引已经是最后一条数据了,那么就不需要移动到最后了 if (lastChild.rank == ranks.Count - 1) { return; } //第一个物体变成实际第二个物体 firstChild = cantent.GetChild(1).GetComponent<PlayerView>(); //第一个物体要移动到最后 //设置子物体的索引到最后一个 Transform temp = cantent.GetChild(0); temp.transform.SetAsLastSibling(); //计算最后一个的索引值, 根据之前最后一个子物体的Rank int lastIndex = lastChild.rank + 1; temp.gameObject.name = "P_" + lastIndex; temp.GetComponent<PlayerView>().SetPlayerData(lastIndex, ranks[lastIndex]); RectTransform rt = temp as RectTransform; rt.anchoredPosition = new Vector2(rt.anchoredPosition.x, -playerTemplateHeight * lastIndex); lastChild = temp.GetComponent<PlayerView>(); } /// <summary> /// 交换最后一个到第一个 /// </summary> void SwitchLastToFirst() { //如果当前第一个已经是第一名了,就不需要交换了 if (firstChild.rank == 0) { return; } //最后一个指向需要变为倒数第二个 lastChild = cantent.GetChild(cantent.childCount - 2).GetComponent<PlayerView>(); Transform temp = cantent.GetChild(cantent.childCount - 1); //将最后一个物体移动到第一个位置 temp.SetAsFirstSibling(); //计算当前第一个应该显示的rank值 int firstIndex = firstChild.rank - 1; temp.gameObject.name = "P_" + firstIndex; temp.GetComponent<PlayerView>().SetPlayerData(firstIndex, ranks[firstIndex]); RectTransform rt = temp as RectTransform; rt.anchoredPosition = new Vector2(rt.anchoredPosition.x, -playerTemplateHeight * firstIndex); firstChild = temp.GetComponent<PlayerView>(); } }
RankModel
using System.Collections; using System.Collections.Generic; using UnityEngine; public class RankModel { public string name; public int level; public int maxScore; public RankModel() { } public RankModel(string name, int level, int maxScore) { this.name = name; this.level = level; this.maxScore = maxScore; } public override string ToString() { return string.Format("name: {0}, level: {1}, maxScore: {2}", name, level, maxScore); } }