182 lines
5.9 KiB
C#
182 lines
5.9 KiB
C#
using System.Collections;
|
|
using System.Reflection;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using UnityEngine.EventSystems;
|
|
using TMPro;
|
|
|
|
public class CustomTMPDropdown : TMP_Dropdown
|
|
{
|
|
private FieldInfo dropdownField;
|
|
private FieldInfo blockerField;
|
|
private GameObject dropdownListGO;
|
|
|
|
private int currentOptionIndex = 0; // 光标在 options 索引
|
|
|
|
protected override void Awake()
|
|
{
|
|
base.Awake();
|
|
|
|
dropdownField = typeof(TMP_Dropdown).GetField(
|
|
"m_Dropdown",
|
|
BindingFlags.NonPublic | BindingFlags.Instance
|
|
);
|
|
blockerField = typeof(TMP_Dropdown).GetField(
|
|
"m_Blocker",
|
|
BindingFlags.NonPublic | BindingFlags.Instance
|
|
);
|
|
}
|
|
|
|
public override void OnPointerClick(PointerEventData eventData) { }
|
|
public override void OnSubmit(BaseEventData eventData) { }
|
|
public override void OnCancel(BaseEventData eventData) { }
|
|
|
|
private GameObject GetDropdownGO() => dropdownField?.GetValue(this) as GameObject;
|
|
private GameObject GetBlockerGO() => blockerField?.GetValue(this) as GameObject;
|
|
|
|
// ---------------- 手动打开/关闭 ----------------
|
|
public void OpenDropdown()
|
|
{
|
|
if (dropdownListGO != null && dropdownListGO.activeSelf) return;
|
|
base.Show();
|
|
StartCoroutine(FixAfterShow());
|
|
}
|
|
|
|
public void CloseDropdown()
|
|
{
|
|
if (dropdownListGO != null) Destroy(dropdownListGO);
|
|
GameObject blocker = GetBlockerGO();
|
|
if (blocker != null) Destroy(blocker);
|
|
dropdownListGO = null;
|
|
}
|
|
|
|
public void ToggleDropdown()
|
|
{
|
|
var list = GetDropdownGO();
|
|
if (list != null && list.activeSelf) CloseDropdown();
|
|
else OpenDropdown();
|
|
}
|
|
|
|
// ---------------- 上下切换 ----------------
|
|
public void SelectNext()
|
|
{
|
|
if (options.Count == 0) return;
|
|
currentOptionIndex = (currentOptionIndex + 1) % options.Count;
|
|
value = currentOptionIndex;
|
|
RefreshShownValue();
|
|
UpdateHighlight();
|
|
}
|
|
|
|
public void SelectPrevious()
|
|
{
|
|
if (options.Count == 0) return;
|
|
currentOptionIndex = (currentOptionIndex - 1 + options.Count) % options.Count;
|
|
value = currentOptionIndex;
|
|
RefreshShownValue();
|
|
UpdateHighlight();
|
|
}
|
|
|
|
// ---------------- 提交 ----------------
|
|
public void SubmitSelection()
|
|
{
|
|
value = currentOptionIndex;
|
|
RefreshShownValue();
|
|
onValueChanged?.Invoke(value);
|
|
CloseDropdown();
|
|
}
|
|
|
|
// ---------------- 初始化 Dropdown ----------------
|
|
private IEnumerator FixAfterShow()
|
|
{
|
|
yield return null; // 等一帧
|
|
|
|
dropdownListGO = GetDropdownGO();
|
|
if (dropdownListGO == null) yield break;
|
|
|
|
// 禁用 Blocker 射线拦截
|
|
GameObject blocker = GetBlockerGO();
|
|
if (blocker != null)
|
|
{
|
|
Button btn = blocker.GetComponent<Button>();
|
|
if (btn != null) btn.onClick.RemoveAllListeners();
|
|
btn.enabled = false;
|
|
|
|
CanvasGroup cg = blocker.GetComponent<CanvasGroup>();
|
|
if (cg == null) cg = blocker.AddComponent<CanvasGroup>();
|
|
cg.blocksRaycasts = false;
|
|
}
|
|
|
|
// 清理 Dropdown List EventTrigger
|
|
var et = dropdownListGO.GetComponent<EventTrigger>();
|
|
if (et != null) et.triggers.Clear();
|
|
|
|
// 遍历生成的选项,绑定点击事件
|
|
Transform content = dropdownListGO.transform.Find("Viewport/Content");
|
|
if (content != null)
|
|
{
|
|
for (int i = 1; i < content.childCount && i <= options.Count; i++) // i=1跳过模板
|
|
{
|
|
GameObject item = content.GetChild(i).gameObject;
|
|
Toggle toggle = item.GetComponent<Toggle>();
|
|
if (toggle != null)
|
|
{
|
|
int optionIndex = i - 1; // Content索引->options索引
|
|
toggle.onValueChanged.RemoveAllListeners();
|
|
toggle.onValueChanged.AddListener((isOn) =>
|
|
{
|
|
if (isOn)
|
|
{
|
|
currentOptionIndex = optionIndex;
|
|
value = currentOptionIndex;
|
|
RefreshShownValue();
|
|
onValueChanged?.Invoke(value);
|
|
CloseDropdown();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// 初始化光标位置
|
|
currentOptionIndex = value;
|
|
UpdateHighlight();
|
|
}
|
|
|
|
// ---------------- 光标高亮 + 滚动 ----------------
|
|
private void UpdateHighlight()
|
|
{
|
|
if (dropdownListGO == null) return;
|
|
|
|
Transform content = dropdownListGO.transform.Find("Viewport/Content");
|
|
if (content == null) return;
|
|
|
|
ScrollRect scrollRect = dropdownListGO.GetComponentInChildren<ScrollRect>();
|
|
|
|
for (int i = 1; i < content.childCount && i <= options.Count; i++) // i=1跳过模板
|
|
{
|
|
GameObject item = content.GetChild(i).gameObject;
|
|
Toggle toggle = item.GetComponent<Toggle>();
|
|
if (toggle != null)
|
|
{
|
|
toggle.SetIsOnWithoutNotify((i - 1) == currentOptionIndex);
|
|
|
|
// 滚动到可见
|
|
if ((i - 1) == currentOptionIndex && scrollRect != null)
|
|
{
|
|
RectTransform itemRect = item.GetComponent<RectTransform>();
|
|
RectTransform contentRect = content.GetComponent<RectTransform>();
|
|
|
|
Vector2 localPos = contentRect.InverseTransformPoint(itemRect.position);
|
|
Vector2 viewportLocalPos = contentRect.InverseTransformPoint(scrollRect.viewport.position);
|
|
|
|
float offset = localPos.y - viewportLocalPos.y;
|
|
float normalized = 1 - Mathf.Clamp01((offset + itemRect.rect.height) / contentRect.rect.height);
|
|
scrollRect.verticalNormalizedPosition = normalized;
|
|
}
|
|
}
|
|
}
|
|
|
|
RefreshShownValue();
|
|
}
|
|
}
|