// Copyright (c) 2025 EFramework Innovation. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using TMPro;
using EFramework.Unity.Utility;
namespace EFramework.Unity.UIPlugin
{
///
/// UIUtility 提供了一系列简化 UI 组件操作的扩展方法,包含组件查找、属性设置、事件处理等功能,是一个 Unity UI 的工具函数集。
///
///
///
/// 功能特性
/// - 快速索引功能:通过路径快速获取 UI 组件的子对象
/// - 显示状态控制:支持透明度、文本、显示隐藏等属性的设置
/// - 扩展方法支持:采用扩展方法设计,函数调用更为直观
///
/// 使用手册
/// 1. 组件查找
///
/// 1. 快速索引:
///
/// // 通过RectTransform查找子组件
/// var button = rectTransform.Index<Button>("ButtonName");
///
/// // 通过Canvas查找子组件
/// var text = canvas.Index<Text>("TextName");
///
/// 2. 透明度控制
///
/// 1. 设置组件透明度:
///
/// // 设置当前组件透明度为0.5
/// gameObject.SetGraphicAlpha(0.5f);
///
/// // 设置子组件透明度为128(0-255范围)
/// gameObject.SetGraphicAlpha("ChildName", 128);
///
/// 3. 事件管理
///
/// 1. 按钮点击事件:
///
/// // 为按钮设置点击事件
/// gameObject.SetButtonEvent((go) => {
/// Debug.Log("按钮被点击:" + go.name);
/// });
///
/// // 启用/禁用按钮交互
/// gameObject.SetEventEnabled(false);
///
/// 4. 文本设置
///
/// 1. 设置普通文本:
///
/// // 设置Text组件内容
/// gameObject.SetLabelText("新文本内容");
///
/// // 设置子对象的Text组件内容
/// gameObject.SetLabelText("ChildText", "新文本内容");
///
/// 2. 设置TMP文本:
///
/// // 设置TextMeshProUGUI组件内容
/// gameObject.SetMeshProText("新文本内容");
///
/// // 设置子对象的TextMeshProUGUI组件内容
/// gameObject.SetMeshProText("ChildTMP", "新文本内容");
///
/// 5. 图片管理
///
/// 1. 设置精灵图片:
///
/// // 从图集中设置图片
/// gameObject.SetSpriteName("ChildImage", "SpriteName", atlasComponent);
///
/// // 设置图片透明度
/// gameObject.SetSpriteAlpha(0.5f);
///
/// 2. 加载网络图片:
///
/// // 加载并显示网络图片
/// gameObject.SetRawImage("http://example.com/image.jpg");
///
/// // 加载网络图片并禁用缓存
/// gameObject.SetRawImage("http://example.com/image.jpg", false);
///
/// // 获取已缓存的贴图
/// var texture = UIUtility.GetTexture("http://example.com/image.jpg");
///
/// 6. 布局刷新
///
/// 1. 刷新UI布局:
///
/// // 重新计算布局
/// gameObject.RefreshObSort();
///
/// // 刷新子对象布局
/// gameObject.RefreshObSort("ChildLayout");
///
/// 更多信息请参考模块文档。
///
public static class UIUtility
{
///
/// Index 快速索引组件。
/// 通过路径查找目标节点并获取指定类型的组件。
///
/// 当前的 RectTransform 组件
/// 目标节点的名称或路径
/// 要获取的组件类型,默认为null
/// 找到的组件实例,如果未找到则返回null
public static object Index(this RectTransform rect, string name, System.Type type = null) { if (rect) return XComp.Index(rect.gameObject, name, type); else return null; }
///
/// Index 快速索引组件的泛型版本。
/// 通过路径查找目标节点并获取指定类型的组件。
///
/// 要获取的组件类型
/// 当前的 RectTransform 组件
/// 目标节点的名称或路径
/// 找到的类型为T的组件实例,如果未找到则返回null
public static T Index(this RectTransform rect, string name) where T : class { if (rect) return XComp.Index(rect.gameObject, name); else return null; }
///
/// Index 快速索引组件。
/// 通过路径在 Canvas 下查找目标节点并获取指定类型的组件。
///
/// 当前的 Canvas 组件。
/// 目标节点的名称或路径。
/// 要获取的组件类型,默认为null。
/// 找到的组件实例,如果未找到则返回null
public static object Index(this Canvas canvas, string name, System.Type type = null) { if (canvas) return XComp.Index(canvas.gameObject, name, type); else return null; }
///
/// Index 快速索引组件的泛型版本。
/// 通过路径在 Canvas 下查找目标节点并获取指定类型的组件。
///
/// 要获取的组件类型
/// 当前的 Canvas 组件
/// 目标节点的名称或路径
/// 找到的类型为T的组件实例,如果未找到则返回null
public static T Index(this Canvas canvas, string name) where T : class { if (canvas) return XComp.Index(canvas.gameObject, name); else return null; }
///
/// SetGraphicAlpha 设置图形组件的透明度。
/// 使用0-255范围的整数值设置当前对象上图形组件的透明度。
///
/// 目标对象
/// 透明度值,范围为0-255,其中0表示完全透明,255表示完全不透明
/// 修改后的 Graphic 组件
public static Graphic SetGraphicAlpha(this Object rootObj, int alpha) { return SetGraphicAlpha(rootObj, null, alpha / 255f); }
///
/// SetGraphicAlpha 设置图形组件的透明度。
/// 使用0-1范围的浮点值设置当前对象上图形组件的透明度。
///
/// 目标对象
/// 透明度值,范围为0-1,其中0表示完全透明,1表示完全不透明
/// 修改后的 Graphic 组件
public static Graphic SetGraphicAlpha(this Object rootObj, float alpha) { return SetGraphicAlpha(rootObj, null, alpha); }
///
/// SetGraphicAlpha 设置图形组件的透明度。
/// 使用0-255范围的整数值设置指定路径对象上图形组件的透明度。
///
/// 父对象
/// 目标对象的路径,相对于父对象
/// 透明度值,范围为0-255,其中0表示完全透明,255表示完全不透明
/// 修改后的 Graphic 组件
public static Graphic SetGraphicAlpha(this Object parentObj, string path, int alpha) { return SetGraphicAlpha(parentObj, path, alpha / 255f); }
///
/// SetGraphicAlpha 设置图形组件的透明度。
/// 使用0-1范围的浮点值设置指定路径对象上图形组件的透明度。
///
/// 父对象
/// 目标对象的路径,相对于父对象
/// 透明度值,范围为0-1,其中0表示完全透明,1表示完全不透明
/// 修改后的 Graphic 组件
public static Graphic SetGraphicAlpha(this Object parentObj, string path, float alpha)
{
var graphic = parentObj.GetComponent(path, typeof(Graphic)) as Graphic;
var color = graphic.color;
color.a = alpha;
graphic.color = color;
return graphic;
}
///
/// SetButtonEvent 设置按钮点击事件。
/// 为当前对象上的按钮组件添加点击事件监听器。
///
/// 目标对象
/// 点击事件回调函数,参数为被点击的游戏对象
/// 设置了事件的 Button 组件
public static Button SetButtonEvent(this Object rootObj, System.Action func) { return SetButtonEvent(rootObj, null, func); }
///
/// SetButtonEvent 设置按钮点击事件。
/// 为指定路径对象上的按钮组件添加点击事件监听器。
///
/// 父对象
/// 目标对象的路径,相对于父对象
/// 点击事件回调函数,参数为被点击的游戏对象
/// 设置了事件的 Button 组件
public static Button SetButtonEvent(this Object parentObj, string path, System.Action func)
{
var listener = parentObj.GetComponent(path, typeof(Button)) as Button;
if (listener) listener.onClick.AddListener(() => { func?.Invoke(listener.gameObject); });
return listener;
}
///
/// SetEventEnabled 设置事件是否启用。
/// 控制当前对象上按钮组件的交互状态。
///
/// 目标对象
/// 是否启用交互,true表示启用,false表示禁用
public static void SetEventEnabled(this Object rootObj, bool enabled)
{
var btn = rootObj.GetComponent(typeof(Button)) as Button;
if (btn) btn.interactable = enabled;
}
///
/// SetEventEnabled 设置事件是否启用。
/// 控制指定路径对象上按钮组件的交互状态。
///
/// 父对象
/// 目标对象的路径,相对于父对象
/// 是否启用交互,true表示启用,false表示禁用
public static void SetEventEnabled(this Object parentObj, string path, bool enabled)
{
var btn = parentObj.GetComponent(path, typeof(Button)) as Button;
if (btn) btn.interactable = enabled;
}
///
/// SetLabelText 设置文本内容。
/// 更新当前对象上 Text 组件的文本内容。
///
/// 目标对象
/// 要设置的文本内容,将自动转换为字符串
/// 设置了内容的 Text 组件
public static Text SetLabelText(this Object rootObj, object content)
{
if (content == null) return null;
var label = rootObj.GetComponent(typeof(Text)) as Text;
if (label)
{
label.text = content.ToString();
return label;
}
return null;
}
///
/// SetLabelText 设置文本内容。
/// 更新指定路径对象上 Text 组件的文本内容。
///
/// 父对象
/// 目标对象的路径,相对于父对象
/// 要设置的文本内容,将自动转换为字符串
/// 设置了内容的 Text 组件
public static Text SetLabelText(this Object parentObj, string path, object content)
{
if (content == null) return null;
var label = parentObj.GetComponent(path, typeof(Text)) as Text;
if (label)
{
label.text = content.ToString();
return label;
}
return null;
}
///
/// SetMeshProText 设置 TextMeshPro 文本内容。
/// 更新当前对象上 TextMeshProUGUI 组件的文本内容。
///
/// 目标对象。
/// 要设置的文本内容,将自动转换为字符串。
/// 设置了内容的 TextMeshProUGUI 组件。
public static TextMeshProUGUI SetMeshProText(this Object rootObj, object content)
{
if (content == null) return null;
var label = rootObj.GetComponent(typeof(TextMeshProUGUI)) as TextMeshProUGUI;
if (label)
{
label.text = content.ToString();
return label;
}
return null;
}
///
/// SetMeshProText 设置 TextMeshPro 文本内容。
/// 更新指定路径对象上 TextMeshProUGUI 组件的文本内容。
///
/// 父对象。
/// 目标对象的路径,相对于父对象。
/// 要设置的文本内容,将自动转换为字符串。
/// 设置了内容的 TextMeshProUGUI 组件。
public static TextMeshProUGUI SetMeshProText(this Object parentObj, string path, object content)
{
if (content == null) return null;
var label = parentObj.GetComponent(path, typeof(TextMeshProUGUI)) as TextMeshProUGUI;
if (label)
{
label.text = content.ToString();
return label;
}
return null;
}
///
/// SetSpriteName 设置图片精灵。
/// 从指定图集中获取精灵并设置到指定路径对象的 Image 组件。
///
/// 父对象。
/// 目标对象的路径,相对于父对象。
/// 要设置的精灵名称。
/// 精灵图集。
/// 设置了精灵的 Image 组件。
public static Image SetSpriteName(this Object parentObj, string path, string name, UIAtlas atlas)
{
var image = parentObj.GetComponent(path, typeof(Image)) as Image;
if (image && atlas)
{
image.sprite = atlas.GetSprite(name);
return image;
}
return null;
}
///
/// SetSpriteAlpha 设置图片精灵的透明度。
/// 使用0-255范围的整数值设置当前对象上Image组件的透明度。
///
/// 目标对象。
/// 透明度值,范围为0-255,其中0表示完全透明,255表示完全不透明。
/// 修改后的 Image 组件。
public static Image SetSpriteAlpha(this Object rootObj, int alpha) { return SetSpriteAlpha(rootObj, null, (float)alpha / 255); }
///
/// SetSpriteAlpha 设置图片精灵的透明度。
/// 使用0-255范围的整数值设置指定路径对象上Image组件的透明度。
///
/// 父对象
/// 目标对象的路径,相对于父对象。
/// 透明度值,范围为0-255,其中0表示完全透明,255表示完全不透明。
/// 修改后的 Image 组件。
public static Image SetSpriteAlpha(this Object parentObj, string path, int alpha) { return SetSpriteAlpha(parentObj, path, (float)alpha / 255); }
///
/// SetSpriteAlpha 设置图片精灵的透明度。
/// 使用0-1范围的浮点值设置当前对象上 Image 组件的透明度。
///
/// 目标对象。
/// 透明度值,范围为0-1,其中0表示完全透明,1表示完全不透明。
/// 修改后的 Image 组件。
public static Image SetSpriteAlpha(this Object rootObj, float alpha) { return SetSpriteAlpha(rootObj, null, alpha); }
///
/// SetSpriteAlpha 设置图片精灵的透明度。
/// 使用0-1范围的浮点值设置指定路径对象上 Image 组件的透明度。
///
/// 父对象。
/// 目标对象的路径,相对于父对象。
/// 透明度值,范围为0-1,其中0表示完全透明,1表示完全不透明。
/// 修改后的 Image 组件。
public static Image SetSpriteAlpha(this Object parentObj, string path, float alpha)
{
var image = parentObj.GetComponent(path, typeof(Image)) as Image;
if (image)
{
var nc = new Color(image.color.r, image.color.g, image.color.b, alpha);
image.color = nc;
}
return image;
}
///
/// SetRawImage 设置原始图片。
/// 从网络URL加载图片并设置到当前对象上的 RawImage 组件,默认使用缓存。
///
/// 目标对象。
/// 图片的网络URL。
/// 设置了图片的 RawImage 组件。
public static RawImage SetRawImage(this Object rootObj, string url) { return SetRawImage(rootObj, url, true); }
///
/// SetRawImage 设置原始图片。
/// 从网络URL加载图片并设置到当前对象上的 RawImage 组件,可控制是否使用缓存。
///
/// 目标对象。
/// 图片的网络URL。
/// 是否使用缓存,true 表示使用,false 表示不使用。
/// 设置了图片的 RawImage 组件。
public static RawImage SetRawImage(this Object rootObj, string url, bool incache)
{
var texture = rootObj.GetComponent(typeof(RawImage)) as RawImage;
if (texture)
{
texture.texture = null;
var done = false;
if (incache)
{
mCachedTextures.TryGetValue(url, out var tex);
if (tex)
{
texture.texture = tex;
done = true;
}
}
if (done == false) XLoom.RunCoroutine(WWWTexture(url, texture, null));
}
return texture;
}
///
/// SetRawImage 设置原始图片。
/// 从网络URL加载图片并设置到指定路径对象上的 RawImage 组件,默认使用缓存。
///
/// 父对象。
/// 目标对象的路径,相对于父对象。
/// 图片的网络 URL。
/// 设置了图片的 RawImage 组件。
public static RawImage SetRawImage(this Object parentObj, string path, string url) { return SetRawImage(parentObj, path, url, true); }
///
/// SetRawImage 设置原始图片。
/// 从网络 URL 加载图片并设置到指定路径对象上的 RawImage 组件,可控制是否使用缓存。
///
/// 父对象。
/// 目标对象的路径,相对于父对象。
/// 图片的网络 URL。
/// 是否使用缓存,true 表示使用,false 表示不使用。
/// 设置了图片的 RawImage 组件。
public static RawImage SetRawImage(this Object parentObj, string path, string url, bool incache)
{
var texture = parentObj.GetComponent(path, typeof(RawImage)) as RawImage;
if (texture)
{
texture.texture = null;
var done = false;
if (incache)
{
mCachedTextures.TryGetValue(url, out var tex);
if (tex)
{
texture.texture = tex;
done = true;
}
}
if (done == false) XLoom.RunCoroutine(WWWTexture(url, texture, null));
}
return texture;
}
///
/// GetTexture 获取已缓存的贴图。
/// 通过 URL 从缓存中获取之前加载过的贴图。
///
/// 贴图的网络 URL。
/// 缓存的贴图,如果未缓存则返回 null。
public static Texture2D GetTexture(string url)
{
Texture2D tex = null;
if (string.IsNullOrEmpty(url) == false) mCachedTextures.TryGetValue(url, out tex);
return tex;
}
///
/// WWWTexture 异步下载贴图。
/// 从网络 URL 加载贴图,并在加载完成后执行回调,可控制是否使用缓存。
///
/// 贴图的网络 URL。
/// 是否使用缓存,true 表示使用,false 表示不使用。
/// 加载完成后的回调函数。
public static void WWWTexture(string url, bool incache, System.Action callback)
{
var done = false;
if (incache)
{
mCachedTextures.TryGetValue(url, out var tex);
if (tex)
{
done = true;
callback?.Invoke();
}
}
if (done == false) XLoom.RunCoroutine(WWWTexture(url, null, callback));
}
///
/// mCachedTextures 贴图缓存字典,用于存储已加载的网络贴图。
/// 键为贴图的URL,值为加载的Texture2D对象。
///
private static readonly Dictionary mCachedTextures = new Dictionary();
///
/// WWWTexture 异步加载贴图的协程方法。
/// 通过UnityWebRequest下载贴图,并在下载完成后更新RawImage组件和缓存,并执行回调。
///
/// 贴图的网络URL
/// 要更新的RawImage组件,可为null
/// 加载完成后的回调函数
/// 协程的迭代器
private static IEnumerator WWWTexture(string url, RawImage texture, System.Action callback)
{
if (string.IsNullOrEmpty(url))
{
callback?.Invoke();
yield break;
}
using var req = UnityWebRequestTexture.GetTexture(url);
yield return req.SendWebRequest();
if (string.IsNullOrEmpty(req.error) == false)
{
XLog.Error("UIUtility.WWWTexture: error info: {0}, url is {1}.", req.error, url);
callback?.Invoke();
yield break;
}
if (req.isDone == false)
{
callback?.Invoke();
yield break;
}
var tex = DownloadHandlerTexture.GetContent(req);
if (tex == null)
{
callback?.Invoke();
yield break;
}
if (mCachedTextures.ContainsKey(url)) mCachedTextures.Remove(url);
mCachedTextures.Add(url, tex);
if (texture) texture.texture = tex;
callback?.Invoke();
}
///
/// RefreshObSort 刷新UI布局。
/// 强制更新指定对象上的布局控制器,重新计算和应用布局。
///
/// 目标对象
/// 目标对象的路径,相对于父对象,默认为空表示当前对象
public static void RefreshObSort(this Object rootObj, string path = "")
{
ILayoutController controller = rootObj.GetComponent(path, typeof(ContentSizeFitter)) as ContentSizeFitter;
if (controller == null) controller = rootObj.GetComponent(path, typeof(HorizontalLayoutGroup)) as HorizontalLayoutGroup;
if (controller != null)
{
controller.SetLayoutHorizontal();
controller.SetLayoutVertical();
}
}
}
}