Unity中如何实现按钮组选中色保留至同组其他按钮被选中
UGUI原生Button的选中高亮状态完全绑定EventSystem的currentSelectedGameObject属性,只要点击任意其他可交互UI组件(包括页面内的功能按钮、输入框、下拉框等),EventSystem会自动将当前选中对象更新为新点击的UI,原底部导航按钮的选中态会被直接清除。这不是组件bug,是原生逻辑的设计目标是适配表单类UI的焦点切换,本来就不符合底部导航常驻选中的交互需求。
之前尝试加Toggle组件无法实现效果,本质是因为Toggle的选中状态同样受EventSystem焦点逻辑控制,且需要配合ToggleGroup做互斥,还要处理Toggle值变化和页面切换的双向同步,冗余逻辑多,不做自定义适配没法直接满足导航栏需求。
第一步:重构导航栏管理逻辑,手动维护选中状态
彻底放弃依赖UGUI原生Button/Toggle的自动选中逻辑,自己控制导航按钮的状态,从根源上和EventSystem的焦点切换逻辑解耦。
把原来挂载在导航栏父物体上的脚本替换为以下代码:
using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; public class BottomNavigationManager : MonoBehaviour { [Header("页面配置,顺序和导航按钮一一对应")] public List<GameObject> Screens = new List<GameObject>(); [Header("底部导航按钮配置,顺序和页面一一对应")] public List<Button> NavButtons = new List<Button>(); public Color normalColor = Color.white; public Color selectedColor = Color.green; private int currentActiveIndex = 0; private void Start() { // 自动给所有导航按钮绑定切换事件 for (int i = 0; i < NavButtons.Count; i++) { int cacheIndex = i; // 闭包缓存索引,避免回调索引错乱 NavButtons[i].onClick.AddListener(() => { SwitchToPage(cacheIndex); }); } // 初始化默认显示第一个页面 SwitchToPage(0); } /// <summary> /// 按索引切换页面 /// </summary> /// <param name="pageIndex">目标页面索引</param> public void SwitchToPage(int pageIndex) { if (pageIndex < 0 || pageIndex >= Screens.Count) return; // 切换页面显隐 for (int i = 0; i < Screens.Count; i++) { Screens[i].SetActive(i == pageIndex); } // 手动刷新所有导航按钮的显示状态 for (int i = 0; i < NavButtons.Count; i++) { ColorBlock buttonColors = NavButtons[i].colors; // 同步所有状态下的颜色,避免悬停、点击时颜色异常 buttonColors.normalColor = i == pageIndex ? selectedColor : normalColor; buttonColors.highlightedColor = i == pageIndex ? selectedColor : normalColor; buttonColors.selectedColor = i == pageIndex ? selectedColor : normalColor; buttonColors.pressedColor = selectedColor; NavButtons[i].colors = buttonColors; // 如果需要切换选中图标、播放选中动效,在这里加逻辑即可 } currentActiveIndex = pageIndex; } // 兼容原有通过传GameObject切换页面的绑定逻辑,不需要修改之前编辑器里的事件配置 public void OnClickBottomNavigationButton(GameObject activeScreen) { int targetIndex = Screens.IndexOf(activeScreen); SwitchToPage(targetIndex); } }
脚本挂好后,在Inspector面板把5个页面对象拖到Screens列表,把5个底部导航按钮拖到NavButtons列表,保证两个列表顺序一一对应即可。
第二步:关闭EventSystem默认的失焦逻辑
选中场景中的EventSystem对象,找到挂载的Standalone Input Module组件,取消勾选Deselect On Background Click选项,解决点击空白区域时选中态丢失的问题。
第三步:效果验证
完成以上配置后,不管点击屏幕空白区域、页面内的其他功能按钮(比如Home页的"Buy Coins"按钮),底部导航栏的选中状态都会常驻在当前激活页面对应的按钮上,不会异常消失。
注意:不要给导航按钮添加Toggle组件,原生Button组件即可满足需求。如果后续需要新增导航按钮/页面,只需要在两个列表中按顺序添加对应引用,不需要修改代码逻辑。
- 初始版本点任意区域就丢选中:一方面是
Standalone Input Module默认开启了点空白取消选中,另一方面点击其他可交互UI时EventSystem会自动切换焦点,原生Button的选中态完全依赖焦点,自然会丢失。 - 之前参考的社区方案只能解决空白区域点击的问题,没有处理其他UI按钮点击时的焦点切换,本质还是没脱离对原生选中逻辑的依赖,所以会出现点其他按钮就丢状态的问题。
内容的提问来源于stack exchange,提问作者chethan venkataramaiah

