HKMBFZ/Assets/Scripts/Szz_Scripts/UI/CustomUI/CustomTMPDropdown.cs

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();
}
}