Unity 2D动态UI:适配鸿蒙不同屏幕尺寸的按钮/文本布局实践_unity的ui布局位置代码getscreenheight
引言
鸿蒙系统覆盖手机(16:9/18:9)、平板(4:3/16:10)、车机(长条形/异形屏)等多尺寸设备,Unity 2D UI若采用固定像素布局,易出现按钮错位、文本溢出或元素重叠等问题。本文将结合鸿蒙屏幕特性与Unity UGUI系统,设计一套动态适配方案,通过代码实现按钮、文本等核心元素的自动缩放与布局调整。
一、鸿蒙屏幕适配的核心挑战
鸿蒙设备的屏幕差异主要体现在三个方面,需针对性解决:
1. 屏幕比例多样性
手机常见16:9(如1080×1920),平板多为16:10(如2560×1600),车机可能采用更长比例(如21:9或自定义异形屏)。固定宽高比的UI布局无法适配所有设备。
2. 分辨率跨度大
从低分辨率手机(720×1280)到高分辨率平板(3840×2160),直接使用像素(Px)定位会导致元素大小在不同设备上差异显著(如100px在小屏手机可能过大,在大屏平板可能过小)。
3. 文本可读性要求
鸿蒙设备用户可能在不同距离使用(如手机贴近面部,车机远距离观看),文本需根据屏幕尺寸动态调整字体大小,确保清晰可读。
二、动态UI布局设计思路
核心目标是让UI元素“基于屏幕尺寸自动调整”,而非依赖固定数值。方案包含以下关键步骤:
1. 基于比例的Canvas缩放
使用Canvas Scaler
组件,设置UI Scale Mode
为Scale With Screen Size
,以目标分辨率(如1920×1080)为基准,通过Reference Resolution
和Screen Match Mode
(匹配宽度/高度/混合)实现全局缩放。
2. 动态布局区域划分
将屏幕划分为“安全区域”(如中间80%区域),避免元素被屏幕圆角、刘海遮挡;使用Horizontal/Vertical Layout Group
或Grid Layout Group
管理子元素,结合Content Size Fitter
实现自适应排列。
3. 百分比定位与动态计算
通过代码获取屏幕宽高比(Screen.width/Screen.height
),动态计算按钮位置、文本字体大小,确保元素在不同比例屏幕上的相对位置一致。
4. 文本自适应优化
使用TextMeshPro
组件替代原生Text,支持自动换行、字体大小自适应(通过Best Fit
功能或代码动态调整)。
三、代码实现:动态按钮与文本布局
1. 基础Canvas配置(Unity编辑器设置)
首先创建Canvas并配置适配参数:
- 渲染模式:
Screen Space - Overlay
(覆盖屏幕,适合2D UI)。 - 添加
Canvas Scaler
组件:UI Scale Mode
→Scale With Screen Size
。Reference Resolution
→ 设计基准分辨率(如1920×1080)。Screen Match Mode
→Match Width or Height
(根据设备选择匹配宽度或高度,或通过代码动态调整)。
2. 动态布局管理器(C#脚本)
创建UILayoutManager
单例,负责监听屏幕尺寸变化并调整UI元素:
// UILayoutManager.csusing UnityEngine;using UnityEngine.UI;using TMPro;public class UILayoutManager : MonoBehaviour { public static UILayoutManager Instance { get; private set; } [Header(\"基准配置\")] [Tooltip(\"设计基准的屏幕宽高比(如1920x1080对应16:9)\")] public Vector2 designAspectRatio = new Vector2(1920f, 1080f); [Tooltip(\"安全区域边距(屏幕边缘预留空间)\")] public float safeAreaMargin = 0.05f; // 5%边距 [Header(\"按钮配置\")] public Button[] dynamicButtons; // 需要动态调整的按钮数组 public float buttonMinWidth = 200f; // 按钮最小宽度(基准分辨率下的像素) public float buttonHeight = 60f; // 按钮固定高度(可根据需求调整) [Header(\"文本配置\")] public TextMeshProUGUI[] dynamicTexts; // 需要动态调整的文本数组 public float minFontSize = 14f; // 最小字体大小 public float maxFontSize = 24f; // 最大字体大小 private void Awake() { if (Instance == null) Instance = this; else Destroy(gameObject); } private void Start() { AdjustLayout(); // 监听屏幕尺寸变化(鸿蒙设备旋转或分辨率切换时触发) Screen.onResolutionChanged += OnResolutionChanged; } private void OnDestroy() { Screen.onResolutionChanged -= OnResolutionChanged; } // 屏幕尺寸变化回调 private void OnResolutionChanged(Vector2Int resolution) { AdjustLayout(); } // 核心布局调整逻辑 public void AdjustLayout() { // 1. 计算当前屏幕宽高比与基准宽高比的匹配度 float currentAspectRatio = (float)Screen.width / Screen.height; float aspectRatioDiff = Mathf.Abs(currentAspectRatio - designAspectRatio.x / designAspectRatio.y); // 2. 调整Canvas Scaler的匹配模式(可选:根据宽高比自动切换匹配宽度/高度) CanvasScaler canvasScaler = FindObjectOfType(); if (canvasScaler != null) { canvasScaler.matchWidthOrHeight = (currentAspectRatio > designAspectRatio.x / designAspectRatio.y) ? 1f // 宽度主导(适合横屏设备) : 0f; // 高度主导(适合竖屏设备) } // 3. 调整按钮布局(位置+大小) AdjustButtons(); // 4. 调整文本布局(字体大小+位置) AdjustTexts(); } // 动态调整按钮位置与大小 private void AdjustButtons() { float safeWidth = Screen.width * (1 - 2 * safeAreaMargin); float safeHeight = Screen.height * (1 - 2 * safeAreaMargin); foreach (Button btn in dynamicButtons) { // 获取按钮原始大小(基准分辨率下的设计值) RectTransform rectTrans = btn.GetComponent(); Vector2 originalSize = rectTrans.sizeDelta; // 动态计算宽度(最小为buttonMinWidth,最大不超过安全区域的80%) float targetWidth = Mathf.Clamp(originalSize.x * (Screen.width / designAspectRatio.x), buttonMinWidth, safeWidth * 0.8f); // 高度保持固定比例(或根据设计需求调整) float targetHeight = originalSize.y * (targetWidth / originalSize.x); // 更新按钮大小 rectTrans.sizeDelta = new Vector2(targetWidth, targetHeight); // 水平居中放置在安全区域底部(Y坐标为安全区域高度 - 按钮高度 - 边距) float xPos = (Screen.width - targetWidth) / 2; float yPos = safeHeight - targetHeight - (Screen.height * safeAreaMargin); rectTrans.anchoredPosition = new Vector2(xPos, yPos); } } // 动态调整文本字体大小与位置 private void AdjustTexts() { float safeWidth = Screen.width * (1 - 2 * safeAreaMargin); float safeHeight = Screen.height * (1 - 2 * safeAreaMargin); foreach (TextMeshProUGUI text in dynamicTexts) { RectTransform rectTrans = text.GetComponent(); // 文本宽度自适应(不超过安全区域的90%) float maxWidth = safeWidth * 0.9f; text.maxWidth = maxWidth; // 自动调整字体大小(使用TextMeshPro的Best Fit功能) text.autoSizeTextContainer = true; text.alignment = TextAlignmentOptions.Center; // 若需手动控制字体大小(替代Best Fit): /* float textWidth = text.preferredWidth; float textHeight = text.preferredHeight; float scale = Mathf.Min(maxWidth / textWidth, safeHeight / textHeight); text.fontSize = Mathf.RoundToInt(Mathf.Clamp(text.fontSize * scale, minFontSize, maxFontSize)); */ } }}
3. 按钮与文本的Unity编辑器配置
在Unity中创建UI元素并绑定脚本:
- 新建Canvas,添加
UILayoutManager
脚本到空物体(如命名为UILayoutRoot
)。 - 创建按钮(Button)并添加到
dynamicButtons
数组:- 设置按钮的
RectTransform
锚点为Middle Center
(中间居中)。 - 添加
Horizontal Layout Group
(可选,用于多按钮横向排列)。
- 设置按钮的
- 创建文本(TextMeshPro - Text (UI))并添加到
dynamicTexts
数组:- 启用
Auto Size
(在Inspector中勾选Auto Size Text
)。 - 设置
Anchor
为Stretch
(拉伸填充父容器)。
- 启用
4. 鸿蒙设备特殊处理(刘海屏/异形屏)
鸿蒙部分设备(如折叠屏、带刘海的手机)存在屏幕安全区域限制,需通过Screen.safeArea
获取安全区域坐标,避免UI被遮挡:
// 在AdjustLayout方法中添加安全区域适配Rect safeArea = Screen.safeArea;Vector2Int safeSize = new Vector2Int( (int)(safeArea.width * Screen.dpi), (int)(safeArea.height * Screen.dpi));// 使用safeSize替代Screen.width/height计算布局// 示例:将按钮放置在安全区域内float safeLeft = safeArea.x * (Screen.width / Screen.currentResolution.width);float safeBottom = safeArea.y * (Screen.height / Screen.currentResolution.height);rectTrans.anchoredPosition = new Vector2(safeLeft + (safeWidth - targetWidth)/2, safeBottom + safeHeight - targetHeight - margin);
四、测试与优化建议
1. 多分辨率测试
在Unity中通过Game
视图的分辨率下拉菜单模拟鸿蒙设备(如1080×1920、2560×1600、2160×1080),验证按钮是否居中、文本是否换行或缩放。
2. 真机调试
通过DevEco Studio连接鸿蒙手机/平板,使用adb logcat
查看是否有布局错误日志(如文本溢出),调整buttonMinWidth
或maxFontSize
参数。
3. 性能优化
- 避免在
AdjustLayout
中频繁调用GetComponent
,可将RectTransform
组件缓存。 - 对于复杂UI(如列表),使用
Recycle ListView
替代多个独立按钮,减少Draw Call。
4. 动态加载布局配置
可通过JSON文件存储不同设备的布局参数(如按钮大小、文本字体),运行时根据设备型号加载对应配置,提升适配灵活性。
总结
通过Canvas Scaler
全局缩放、动态计算元素位置/大小,结合代码对屏幕宽高比和分辨率的实时响应,可有效解决鸿蒙多尺寸设备的UI适配问题。核心原则是“相对布局优于绝对定位”,通过百分比、安全区域和动态计算确保UI在不同设备上的一致性与可用性。