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

413 lines
14 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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