using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
///
/// 自定义键盘管理器 - 处理任意 TMP_InputField 的键盘弹出
/// 支持不同键盘布局:拼音(中文)、数字、字母等
///
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 _onConfirm;
private Canvas _keyboardCanvas; // 键盘的 Canvas 组件
void Awake()
{
if (_instance == null)
{
_instance = this;
// 启动拼音词库加载
StartCoroutine(PinyinInputManager.Initialize());
}
else if (_instance != this)
{
Destroy(gameObject);
}
}
///
/// 为 InputField 显示自定义键盘
///
/// 目标输入框
/// 键盘布局类型
/// 确认回调(可选)
/// 首次输入时是否清空原内容
public void ShowKeyboard(TMP_InputField inputField, KeyboardLayout layout, Action onConfirm = null, bool clearOnFirstInput = false)
{
if (inputField == null) return;
// 如果已有键盘,先隐藏(这会清空 _targetInputField)
HideKeyboard();
// 在隐藏旧键盘后再设置新的目标(避免被 HideKeyboard 清空)
_targetInputField = inputField;
_onConfirm = onConfirm;
// 创建键盘面板
_currentKeyboardPanel = CreateKeyboardPanel();
_currentKeyboard = _currentKeyboardPanel.GetComponent();
if (_currentKeyboard != null)
{
// 使用适配器模式让 CustomKeyboard 直接修改 InputField
var adapter = _currentKeyboardPanel.AddComponent();
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(); // 取消激活状态
}
///
/// 根据键盘布局自动计算大小
///
private void AdjustKeyboardSize(KeyboardLayout layout)
{
if (_currentKeyboardPanel == null || _currentKeyboard == null) return;
var panelRT = _currentKeyboardPanel.GetComponent();
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}");
}
///
/// 获取当前键盘所需的行数(用于计算键盘高度)
///
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;
}
///
/// 调整键盘位置避免遮挡输入框
///
private void AdjustKeyboardPosition(TMP_InputField inputField)
{
if (_currentKeyboardPanel == null || inputField == null) return;
var panelRT = _currentKeyboardPanel.GetComponent();
var inputRT = inputField.GetComponent();
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;
}
}
///
/// 隐藏当前键盘
///
public void HideKeyboard()
{
if (_currentKeyboardRoot != null)
{
Destroy(_currentKeyboardRoot);
_currentKeyboardRoot = null;
_currentKeyboard = null;
_keyboardCanvas = null;
}
else if (_currentKeyboardPanel != null)
{
Destroy(_currentKeyboardPanel);
}
_currentKeyboardPanel = null;
_targetInputField = null;
}
///
/// 确认输入
///
public void ConfirmInput()
{
if (_targetInputField != null)
{
_onConfirm?.Invoke(_targetInputField.text);
}
HideKeyboard();
}
///
/// 设置键盘位置
///
public void SetKeyboardPosition(Vector2 position)
{
if (_currentKeyboardPanel != null)
{
var panelRT = _currentKeyboardPanel.GetComponent();
if (panelRT != null)
{
panelRT.anchoredPosition = position;
}
}
}
///
/// 设置键盘层级
///
public void SetKeyboardSortingOrder(int sortingOrder)
{
if (_keyboardCanvas != null)
{
_keyboardCanvas.sortingOrder = sortingOrder;
}
}
///
/// 获取当前键盘是否显示
///
public bool IsKeyboardShowing()
{
return _currentKeyboardRoot != null && _currentKeyboardRoot.activeInHierarchy;
}
///
/// 创建键盘面板
///
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();
rootRT.anchorMin = Vector2.zero;
rootRT.anchorMax = Vector2.one;
rootRT.offsetMin = Vector2.zero;
rootRT.offsetMax = Vector2.zero;
// 添加 Canvas 以控制层级(在 DialogLayer 之上)
_keyboardCanvas = rootGO.AddComponent