DCS/ruiyiweiUX/Assets/Scripts/UI/Dialogs/CustomKeyboardManager.cs

413 lines
14 KiB
C#
Raw Permalink Normal View History

2026-06-09 13:59:11 +08:00
using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
/// <summary>
/// 自定义键盘管理器 - 处理任意 TMP_InputField 的键盘弹出
/// 支持不同键盘布局:拼音(中文)、数字、字母等
/// </summary>
public class CustomKeyboardManager : MonoBehaviour
{
[Header("键盘面板配置")]
public Transform KeyboardContainer; // 键盘放置的容器(如果为空则使用 UIManager 的 DialogLayer
public GameObject KeyPrefab; // 按键预制体(可选)
public Vector2 KeyboardSize = new Vector2(900, 350);
public Vector2 KeySize = new Vector2(60, 60);
public Vector2 KeySpacing = new Vector2(6, 6);
public int KeysPerRow = 10;
[Header("键盘位置")]
public Vector2 KeyboardAnchoredPosition = new Vector2(0, 50); // 键盘相对锚点的偏移
private static CustomKeyboardManager _instance;
public static CustomKeyboardManager Instance => _instance;
private GameObject _currentKeyboardPanel;
private GameObject _currentKeyboardRoot;
private CustomKeyboard _currentKeyboard;
private TMP_InputField _targetInputField;
private Action<string> _onConfirm;
private Canvas _keyboardCanvas; // 键盘的 Canvas 组件
void Awake()
{
if (_instance == null)
{
_instance = this;
// 启动拼音词库加载
StartCoroutine(PinyinInputManager.Initialize());
}
else if (_instance != this)
{
Destroy(gameObject);
}
}
/// <summary>
/// 为 InputField 显示自定义键盘
/// </summary>
/// <param name="inputField">目标输入框</param>
/// <param name="layout">键盘布局类型</param>
/// <param name="onConfirm">确认回调(可选)</param>
/// <param name="clearOnFirstInput">首次输入时是否清空原内容</param>
public void ShowKeyboard(TMP_InputField inputField, KeyboardLayout layout, Action<string> onConfirm = null, bool clearOnFirstInput = false)
{
if (inputField == null) return;
// 如果已有键盘,先隐藏(这会清空 _targetInputField
HideKeyboard();
// 在隐藏旧键盘后再设置新的目标(避免被 HideKeyboard 清空)
_targetInputField = inputField;
_onConfirm = onConfirm;
// 创建键盘面板
_currentKeyboardPanel = CreateKeyboardPanel();
_currentKeyboard = _currentKeyboardPanel.GetComponent<CustomKeyboard>();
if (_currentKeyboard != null)
{
// 使用适配器模式让 CustomKeyboard 直接修改 InputField
var adapter = _currentKeyboardPanel.AddComponent<KeyboardInputAdapter>();
adapter.SetTarget(inputField, this, clearOnFirstInput);
_currentKeyboard.KeyPrefab = KeyPrefab;
_currentKeyboard.KeySize = KeySize;
_currentKeyboard.KeySpacing = KeySpacing;
_currentKeyboard.SetTarget(adapter, layout, false);
// 根据布局自动计算键盘大小
AdjustKeyboardSize(layout);
}
// 调整键盘位置避免遮挡输入框
AdjustKeyboardPosition(inputField);
// 完全禁用 InputField 的系统键盘和交互
inputField.shouldHideMobileInput = true;
inputField.readOnly = true; // 设为只读,防止系统键盘弹出
inputField.DeactivateInputField(); // 取消激活状态
}
/// <summary>
/// 根据键盘布局自动计算大小
/// </summary>
private void AdjustKeyboardSize(KeyboardLayout layout)
{
if (_currentKeyboardPanel == null || _currentKeyboard == null) return;
var panelRT = _currentKeyboardPanel.GetComponent<RectTransform>();
if (panelRT == null) return;
// 获取键盘所需的行数
int rows = GetKeyboardRows(layout);
// 计算高度:每行高度 = 按键高度 + 间距 + 内边距
float rowHeight = KeySize.y + KeySpacing.y;
float colWidth = KeySize.x + KeySpacing.x;
float totalHeight = rows * rowHeight + 20; // 20 是上下内边距
// 宽度保持不变,只调整高度
Vector2 newSize;
if(layout == KeyboardLayout.Pinyin)
{
newSize = new Vector2(KeyboardSize.x, totalHeight);
}else{
newSize = new Vector2(colWidth * KeysPerRow + 20, totalHeight);
}
panelRT.sizeDelta = newSize;
// Debug.Log($"Keyboard size adjusted: layout={layout}, rows={rows}, height={totalHeight}");
}
/// <summary>
/// 获取当前键盘所需的行数(用于计算键盘高度)
/// </summary>
public int GetKeyboardRows(KeyboardLayout _layout)
{
int keyCount = 0;
switch (_layout)
{
case KeyboardLayout.Numeric:
keyCount = 12; // 数字键盘
break;
case KeyboardLayout.Hex:
keyCount = 16; // 十六进制
break;
case KeyboardLayout.Pinyin:
keyCount = 26 + 1; // 26个字母 + 空格
break;
default:
keyCount = 39; // 默认键盘
break;
}
// 加上控制行Clear, Backspace, OK
keyCount += 3;
// 假设每行10个按键
int cols = KeysPerRow;
int rows = Mathf.CeilToInt((float)keyCount / cols);
// 拼音键盘需要额外的候选字行
if (_layout == KeyboardLayout.Pinyin)
{
rows += 1; // 候选字区域
}
return rows;
}
/// <summary>
/// 调整键盘位置避免遮挡输入框
/// </summary>
private void AdjustKeyboardPosition(TMP_InputField inputField)
{
if (_currentKeyboardPanel == null || inputField == null) return;
var panelRT = _currentKeyboardPanel.GetComponent<RectTransform>();
var inputRT = inputField.GetComponent<RectTransform>();
if (panelRT == null || inputRT == null) return;
// 获取输入框在屏幕上的位置
Vector3[] inputCorners = new Vector3[4];
inputRT.GetWorldCorners(inputCorners);
float inputBottomY = inputCorners[0].y; // 输入框底部 Y 坐标
// 获取键盘高度
float keyboardHeight = KeyboardSize.y;
// 如果输入框位置较低,键盘可能会遮挡,则向上调整
if (inputBottomY < keyboardHeight + 20) // 20 是安全边距
{
// 键盘放在输入框上方
float newY = inputBottomY + keyboardHeight / 2 + 10;
panelRT.anchoredPosition = new Vector2(KeyboardAnchoredPosition.x, newY);
}
else
{
// 使用默认位置(底部)
panelRT.anchoredPosition = KeyboardAnchoredPosition;
}
}
/// <summary>
/// 隐藏当前键盘
/// </summary>
public void HideKeyboard()
{
if (_currentKeyboardRoot != null)
{
Destroy(_currentKeyboardRoot);
_currentKeyboardRoot = null;
_currentKeyboard = null;
_keyboardCanvas = null;
}
else if (_currentKeyboardPanel != null)
{
Destroy(_currentKeyboardPanel);
}
_currentKeyboardPanel = null;
_targetInputField = null;
}
/// <summary>
/// 确认输入
/// </summary>
public void ConfirmInput()
{
if (_targetInputField != null)
{
_onConfirm?.Invoke(_targetInputField.text);
}
HideKeyboard();
}
/// <summary>
/// 设置键盘位置
/// </summary>
public void SetKeyboardPosition(Vector2 position)
{
if (_currentKeyboardPanel != null)
{
var panelRT = _currentKeyboardPanel.GetComponent<RectTransform>();
if (panelRT != null)
{
panelRT.anchoredPosition = position;
}
}
}
/// <summary>
/// 设置键盘层级
/// </summary>
public void SetKeyboardSortingOrder(int sortingOrder)
{
if (_keyboardCanvas != null)
{
_keyboardCanvas.sortingOrder = sortingOrder;
}
}
/// <summary>
/// 获取当前键盘是否显示
/// </summary>
public bool IsKeyboardShowing()
{
return _currentKeyboardRoot != null && _currentKeyboardRoot.activeInHierarchy;
}
/// <summary>
/// 创建键盘面板
/// </summary>
private GameObject CreateKeyboardPanel()
{
// 优先使用 EnhancedUIManager 的 DialogLayer
Transform container = KeyboardContainer;
if (container == null && EnhancedUIManager.Instance != null)
{
container = EnhancedUIManager.Instance.DialogLayer;
}
if (container == null)
{
container = this.transform;
}
var rootGO = new GameObject("CustomKeyboardRoot", typeof(RectTransform));
rootGO.transform.SetParent(container, false);
_currentKeyboardRoot = rootGO;
var rootRT = rootGO.GetComponent<RectTransform>();
rootRT.anchorMin = Vector2.zero;
rootRT.anchorMax = Vector2.one;
rootRT.offsetMin = Vector2.zero;
rootRT.offsetMax = Vector2.zero;
// 添加 Canvas 以控制层级(在 DialogLayer 之上)
_keyboardCanvas = rootGO.AddComponent<Canvas>();
_keyboardCanvas.overrideSorting = true;
_keyboardCanvas.sortingOrder = 350; // 高于 DialogLayer (300) 但低于 SystemLayer (400)
rootGO.AddComponent<UnityEngine.UI.GraphicRaycaster>();
var blockerGO = new GameObject("KeyboardOutsideDismissArea", typeof(RectTransform), typeof(Image), typeof(EventTrigger));
blockerGO.transform.SetParent(rootGO.transform, false);
var blockerRT = blockerGO.GetComponent<RectTransform>();
blockerRT.anchorMin = Vector2.zero;
blockerRT.anchorMax = Vector2.one;
blockerRT.offsetMin = Vector2.zero;
blockerRT.offsetMax = Vector2.zero;
var blockerImage = blockerGO.GetComponent<Image>();
blockerImage.color = new Color(0f, 0f, 0f, 0f);
blockerImage.raycastTarget = true;
var blockerTrigger = blockerGO.GetComponent<EventTrigger>();
var blockerClick = new EventTrigger.Entry { eventID = EventTriggerType.PointerClick };
blockerClick.callback.AddListener(_ => HideKeyboard());
blockerTrigger.triggers.Add(blockerClick);
var panelGO = new GameObject("CustomKeyboardPanel", typeof(RectTransform));
panelGO.transform.SetParent(rootGO.transform, false);
var panelRT = panelGO.GetComponent<RectTransform>();
panelRT.sizeDelta = KeyboardSize;
// 底部居中锚点
panelRT.anchorMin = new Vector2(0.5f, 0);
panelRT.anchorMax = new Vector2(0.5f, 0);
panelRT.pivot = new Vector2(0.5f, 0);
panelRT.anchoredPosition = KeyboardAnchoredPosition;
// 添加背景
var panelImg = panelGO.AddComponent<Image>();
panelImg.color = new Color(0.08f, 0.1f, 0.12f, 0.95f);
// 添加圆角和阴影效果(可选,让键盘更美观)
var shadow = panelGO.AddComponent<UnityEngine.UI.Shadow>();
shadow.effectColor = new Color(0, 0, 0, 0.5f);
shadow.effectDistance = new Vector2(3, -3);
// 创建按键容器
var containerGO = new GameObject("KeysContainer", typeof(RectTransform));
containerGO.transform.SetParent(panelGO.transform, false);
var containerRT = containerGO.GetComponent<RectTransform>();
containerRT.anchorMin = Vector2.zero;
containerRT.anchorMax = Vector2.one;
containerRT.offsetMin = new Vector2(10, 10);
containerRT.offsetMax = new Vector2(-10, -10);
// 添加 GridLayoutGroup
var grid = containerGO.AddComponent<GridLayoutGroup>();
grid.cellSize = KeySize;
grid.spacing = KeySpacing;
grid.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
grid.constraintCount = 10; // 每行10个按键
// 添加 CustomKeyboard 脚本
var keyboard = panelGO.AddComponent<CustomKeyboard>();
keyboard.KeysContainer = containerGO.transform;
keyboard.KeySize = KeySize;
keyboard.KeySpacing = KeySpacing;
return panelGO;
}
/// <summary>
/// 为 InputField 设置点击监听
/// </summary>
public static void SetupInputField(TMP_InputField inputField, KeyboardLayout layout, Action<string> onConfirm = null, bool clearOnFirstInput = false)
{
if (inputField == null)
{
Debug.LogError("CustomKeyboardManager.SetupInputField: inputField is null!");
return;
}
if (inputField.gameObject == null)
{
Debug.LogError("CustomKeyboardManager.SetupInputField: inputField.gameObject is null!");
return;
}
// 检查 Instance 是否存在
if (_instance == null)
{
Debug.LogError("CustomKeyboardManager.SetupInputField: Instance is null. Make sure CustomKeyboardManager exists in scene and Awake() has been called.");
return;
}
// 禁用系统键盘
inputField.shouldHideMobileInput = true;
inputField.readOnly = true;
// 移除已有的 onSelect 事件
inputField.onSelect.RemoveAllListeners();
// 添加 onSelect 事件监听InputField 被选中/点击时触发)
inputField.onSelect.AddListener((text) =>
{
if (Instance != null)
{
Instance.ShowKeyboard(inputField, layout, onConfirm, clearOnFirstInput);
}
else
{
Debug.LogError("CustomKeyboardManager Instance is null when trying to show keyboard.");
}
});
// 确保 InputField 本身不响应点击激活
var eventTrigger = inputField.GetComponent<UnityEngine.EventSystems.EventTrigger>();
if (eventTrigger != null)
{
Destroy(eventTrigger);
}
}
}