Unity移动端InputField自动唤出软键盘:EventTrigger与TouchScreenKeyboard深度集成方案

📅 2026/8/1 8:44:40
Unity移动端InputField自动唤出软键盘:EventTrigger与TouchScreenKeyboard深度集成方案
1. 项目概述告别手动点击的繁琐交互在Unity里做移动端项目尤其是涉及到大量表单输入的应用比如登录注册、聊天室或者设置界面有一个体验细节总是绕不开点击输入框InputField后手动去点屏幕上的软键盘按钮把它呼出来。这个操作看似简单但在追求流畅用户体验的今天每次都需要用户多点一下尤其是在需要频繁切换输入的场景下就显得格外割裂和低效。更别提有些新手开发者可能会直接依赖Unity自带的TouchScreenKeyboard.Open()却发现它在不同平台、不同输入法下的行为并不完全可控有时甚至会出现键盘遮挡输入框的尴尬情况。这个项目的核心目标就是彻底解决这个问题。我们不再满足于Unity InputField默认的半自动行为而是要实现一种“指哪打哪”的智能响应用户的手指只要触碰到InputField虚拟键盘就应该像被施了魔法一样立刻、准确地出现在它该在的位置并且与输入框完美联动。这不仅仅是调用一个API那么简单它涉及到UI事件系统的深度定制、屏幕坐标的精准换算以及对不同移动设备输入环境的兼容性处理。通过结合InputField组件与EventTrigger事件触发器我们可以构建一个既稳定又灵活的自动唤出软键盘方案让应用的输入体验提升一个档次。2. 核心思路与方案选型为什么是EventTrigger在动手之前我们先来拆解一下需求。Unity的UGUI InputField本身是有onEndEdit和onValueChanged等事件的但它缺少一个纯粹的、用于捕获点击PointerDown的事件回调。这正是我们需要EventTrigger的原因。EventTrigger是一个万能的事件转发器它可以监听UGUI事件系统中的几乎所有事件类型包括PointerClick、PointerDown、PointerUp、Select当InputField被选中时等。为什么不直接用OnClick事件呢对于Button组件可以但InputField的OnClick事件并不是原生暴露的。而EventTrigger给了我们挂载自定义逻辑的钩子。我们的核心思路就是为InputField挂载EventTrigger组件监听PointerDown事件它比PointerClick响应更早更符合“点击即响应”的直觉在事件回调中执行唤出系统软键盘的逻辑并确保InputField同时进入可编辑的聚焦状态。这里有几个备选方案和取舍考量继承并重写InputField这是一个更“重量级”的方案。通过创建自定义的MyInputField类重写其OnPointerDown方法。优点是逻辑内聚封装性好。缺点是增加了新的组件类型在团队协作或项目迁移时需要替换所有现有的InputField不够灵活。使用IPointerClickHandler接口为InputField所在的GameObject挂载一个实现了IPointerClickHandler接口的脚本。这比EventTrigger更轻量代码也更集中。但缺点是每个需要此功能的InputField都要挂一个脚本管理起来稍显繁琐且对于同时需要监听多种事件如按下、抬起、进入的场景扩展性不如EventTrigger。使用EventTrigger这正是我们选择的方案。它的优势在于非侵入性和高灵活性。我们不需要修改预制件结构只需添加一个组件不需要创建新的组件类。通过一个统一的控制器脚本比如叫KeyboardManager来响应所有InputField的EventTrigger事件可以实现集中管理。未来如果想增加长按弹出菜单、滑动调整光标等功能只需在EventTrigger上添加新的事件监听即可扩展性极强。注意在移动平台上直接使用TouchScreenKeyboard.Open(“”, TouchScreenKeyboardType.Default, false, false, false, false)是最基础的唤出方式。但我们的方案要做得更精细比如在唤出前需要确保InputField是激活且可交互的有时还需要根据InputField的内容类型数字、邮箱、密码来指定不同的键盘类型。3. 环境准备与基础组件配置在开始写代码之前我们需要在Unity编辑器中搭建好基础的UI环境。这里假设你已经有一个Canvas并且创建了一个标准的InputField。3.1 创建UI与添加EventTrigger在Hierarchy中右键 - UI - Input Field - TextMeshPro推荐使用TextMeshPro版本显示效果更好。如果你使用的是旧版UGUI Text步骤类似。选中刚刚创建的InputField GameObject在Inspector面板中点击“Add Component”按钮。在搜索框中输入“Event Trigger”并添加该组件。添加后你会看到组件下方有一个“Triggers”列表目前是空的。3.2 配置EventTrigger监听事件这是关键的一步。我们需要告诉EventTrigger当哪种事件发生时去通知谁。在EventTrigger组件的“Triggers”列表下方点击“Add New Event Type”。从弹出的下拉列表中选择“Pointer Down”。这个事件在用户手指或鼠标在UI元素上按下的那一刻触发比完整的“Click”更及时。添加后你会看到一个“Pointer Down”事件条目其下有一个运行时Runtime的调用列表。我们需要将包含控制逻辑的脚本拖拽到这里。通常我们会创建一个名为UIManager或InputFieldKeyboardHelper的全局管理脚本挂载在Canvas或一个不会销毁的GameObject上。但为了解耦和复用更推荐创建一个专用于处理InputField键盘事件的脚本例如AutoShowKeyboard直接挂载在InputField本身或它的父节点上。假设我们创建了AutoShowKeyboard脚本。将这个脚本挂载到InputField GameObject上。回到EventTrigger的“Pointer Down”事件配置区点击“”号添加一个调用。将Hierarchy中的InputField GameObject拖到“Object”字段。在“Function”下拉菜单中选择你脚本中用于处理点击的方法例如AutoShowKeyboard - OnInputFieldPointerDown()。至此UI层面的配置就完成了。我们建立了一个链路用户点击InputField - EventTrigger捕获到PointerDown事件 - 调用AutoShowKeyboard脚本中的指定方法。4. 核心脚本实现与逻辑详解接下来我们编写AutoShowKeyboard脚本的核心逻辑。这个脚本需要处理几件事响应点击事件、唤出系统键盘、处理键盘与输入框的联动如隐藏键盘后的逻辑。4.1 脚本结构与基础字段using UnityEngine; using UnityEngine.EventSystems; // 需要引入事件系统命名空间 using UnityEngine.UI; using TMPro; // 如果使用TextMeshPro [RequireComponent(typeof(EventTrigger))] // 确保挂载此脚本的物体有EventTrigger public class AutoShowKeyboard : MonoBehaviour, IPointerDownHandler { // 关联的InputField组件支持旧版UI和TextMeshPro private TMP_InputField _tmpInputField; private InputField _legacyInputField; // 当前激活的键盘实例 private TouchScreenKeyboard _keyboard; // 可配置的键盘类型 public TouchScreenKeyboardType keyboardType TouchScreenKeyboardType.Default; // 是否自动校正 public bool autocorrection true; // 是否用星号隐藏密码 public bool secure false; // 是否在键盘显示时同时选中所有文本 public bool selectAllOnShow false; void Awake() { // 尝试获取TextMeshPro InputField _tmpInputField GetComponentTMP_InputField(); // 如果没找到再尝试获取旧版InputField if (_tmpInputField null) { _legacyInputField GetComponentInputField(); } // 如果两个都没找到报个警告 if (_tmpInputField null _legacyInputField null) { Debug.LogWarning(AutoShowKeyboard 脚本未找到 InputField 或 TMP_InputField 组件。, this); } } }这里我们让脚本同时实现了IPointerDownHandler接口。这样我们就有两种方式触发逻辑一是通过EventTrigger配置更灵活可可视化二是直接通过接口代码更简洁。在Awake中我们尝试获取两种类型的InputField组件以增强兼容性。4.2 响应点击与唤出键盘这是最核心的方法。我们通过实现IPointerDownHandler接口的OnPointerDown方法来响应点击。public void OnPointerDown(PointerEventData eventData) { ShowKeyboard(); } // 也可以作为一个公开方法供EventTrigger调用 public void ShowKeyboard() { // 1. 确保InputField可交互且被激活 if (_tmpInputField ! null _tmpInputField.interactable) { _tmpInputField.ActivateInputField(); // 激活并聚焦 if (selectAllOnShow) { _tmpInputField.SelectAll(); // 可选选中全部文本 } } else if (_legacyInputField ! null _legacyInputField.interactable) { _legacyInputField.ActivateInputField(); if (selectAllOnShow) { _legacyInputField.SelectAll(); } } else { return; // 如果InputField不可用直接返回 } // 2. 根据InputField的类型决定键盘的初始文本 string initialText ; if (_tmpInputField ! null) { initialText _tmpInputField.text; // 如果是密码框设置secure和keyboardType if (_tmpInputField.contentType TMP_InputField.ContentType.Password) { secure true; keyboardType TouchScreenKeyboardType.Default; } } else if (_legacyInputField ! null) { initialText _legacyInputField.text; if (_legacyInputField.contentType InputField.ContentType.Password) { secure true; keyboardType TouchScreenKeyboardType.Default; } } // 3. 打开系统软键盘 _keyboard TouchScreenKeyboard.Open( initialText, keyboardType, autocorrection, false, // multiline 通常为false secure, false, // alert通常为false // 占位符文本iOS有效 ); // 4. 开始协程监听键盘状态 StartCoroutine(MonitorKeyboard()); }关键点解析ActivateInputField(): 这个方法至关重要。它不仅仅是将光标聚焦到输入框还会在移动平台上尝试唤起系统键盘。但它的行为有时不够可靠尤其是在复杂的UI栈中。因此我们将其与TouchScreenKeyboard.Open结合使用双重保障。TouchScreenKeyboard.Open: 这是Unity提供的跨平台API。参数依次是初始文本、键盘类型、是否自动校正、是否多行、是否安全输入密码、是否以警告样式显示iOS、占位符。我们通过脚本的公开变量暴露了前几个常用参数方便在Inspector中针对不同的输入框进行配置比如数字键盘、邮箱键盘。密码框的特殊处理我们检查了InputField的contentType如果是密码类型则强制将secure设为true并将keyboardType设为Default因为数字键盘等不适合密码输入。这是一个很实用的细节处理。4.3 监听键盘状态与同步文本打开键盘只是第一步我们还需要知道用户什么时候完成输入点击了完成/回车键或者隐藏了键盘以便将键盘上输入的内容同步回InputField并执行一些收尾工作比如失去焦点。这里我们使用协程来轮询键盘状态。using System.Collections; private System.Collections.IEnumerator MonitorKeyboard() { if (_keyboard null) yield break; // 等待直到键盘不再为null理论上立即进入 while (_keyboard ! null !_keyboard.done !_keyboard.wasCanceled) { // 实时将键盘文本同步到InputField SyncTextFromKeyboard(); yield return null; // 每帧检查一次 } // 循环结束意味着键盘输入完成done或取消wasCanceled OnKeyboardClosed(); } private void SyncTextFromKeyboard() { if (_keyboard null) return; string keyboardText _keyboard.text; if (_tmpInputField ! null) { // 避免不必要的赋值减少UI重绘 if (_tmpInputField.text ! keyboardText) { _tmpInputField.text keyboardText; // 确保光标在文本末尾 _tmpInputField.caretPosition keyboardText.Length; _tmpInputField.selectionAnchorPosition keyboardText.Length; _tmpInputField.selectionFocusPosition keyboardText.Length; } } else if (_legacyInputField ! null) { if (_legacyInputField.text ! keyboardText) { _legacyInputField.text keyboardText; // 旧版InputField的caretPosition是只读的需要通过其他方式控制这里主要同步文本 } } } private void OnKeyboardClosed() { // 同步最后一次文本 SyncTextFromKeyboard(); // 执行InputField的“完成编辑”逻辑 if (_tmpInputField ! null) { // 手动触发onEndEdit事件如果它有监听器的话 // _tmpInputField.onEndEdit?.Invoke(_tmpInputField.text); _tmpInputField.DeactivateInputField(); // 取消激活隐藏光标 } else if (_legacyInputField ! null) { // _legacyInputField.onEndEdit?.Invoke(_legacyInputField.text); _legacyInputField.DeactivateInputField(); } Debug.Log(软键盘已关闭。); _keyboard null; // 清理引用 }关键点解析TouchScreenKeyboard.done: 当用户按下键盘上的“完成”或回车、搜索等确认键时此属性变为true。TouchScreenKeyboard.wasCanceled: 当用户取消输入如点击键盘外的区域或设备的返回键时此属性变为true。实时同步在MonitorKeyboard协程中我们每帧都将_keyboard.text同步到InputField。这提供了即时的输入反馈。但要注意性能我们通过判断文本是否改变来避免每帧都赋值触发UI重建。光标位置对于TMP_InputField我们手动将光标位置和选择锚点设置到文本末尾这样能保证输入体验连贯。旧版InputField的caretPosition操作受限同步文本是主要目标。DeactivateInputField(): 在键盘关闭后调用它会移除InputField的焦点隐藏闪烁的光标。5. 高级功能与体验优化基础功能实现后我们可以进一步打磨细节提升稳定性和用户体验。5.1 处理UI遮挡与滚动视图适配一个常见的问题是当软键盘弹出时可能会遮挡住当前激活的InputField。尤其是在ScrollRect滚动视图内的输入框。我们需要在唤出键盘后自动将输入框滚动到可视区域内。我们可以创建一个辅助方法在ShowKeyboard方法中唤出键盘后延迟一帧使用Coroutine或Invoke执行滚动适配。public void ShowKeyboard() { // ... 原有的激活和打开键盘代码 ... // 在打开键盘后开始适配视图的协程 StartCoroutine(AdjustViewForKeyboard()); } private IEnumerator AdjustViewForKeyboard() { // 等待一帧确保键盘已经弹出并影响了屏幕布局 yield return null; // 查找输入框所在的ScrollRect ScrollRect parentScrollRect GetComponentInParentScrollRect(); if (parentScrollRect ! null parentScrollRect.viewport ! null) { RectTransform inputFieldRect GetComponentRectTransform(); Canvas canvas GetComponentInParentCanvas(); // 将InputField的世界坐标转换为Viewport内的标准化视口坐标 (0-1) Vector2 viewportPosition RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, inputFieldRect.position); viewportPosition parentScrollRect.viewport.InverseTransformPoint(viewportPosition); // 简单策略如果InputField的底部在Viewport的下半部分则可能被键盘遮挡 // 这里可以计算更精确的遮挡区域但需要获取键盘高度移动平台API各异较复杂 // 一个简化方案强制将InputField滚动到Viewport顶部附近 float targetNormalizedPosition 1.0f; // 默认滚动到最底部因为Content通常从上到下排列 // 需要根据实际的滚动方向调整这是一个需要根据项目UI结构细化的部分 // parentScrollRect.verticalNormalizedPosition targetNormalizedPosition; } }注意精确计算键盘高度并滚动到完美位置是一个复杂课题涉及平台原生插件。一个更实用的方案是设计UI时将需要输入的区域放在屏幕上半部分或者使用ContentSizeFitter和布局组件让ScrollRect自动管理内容。5.2 处理多点触控与异常状态在移动设备上用户可能快速连续点击多个输入框或者在键盘弹出时点击其他非输入区域。我们需要增加一些保护逻辑。防止重复唤出在ShowKeyboard方法开头检查是否已经有一个键盘实例正在显示。if (_keyboard ! null !_keyboard.done !_keyboard.wasCanceled) { // 如果键盘已经存在且未关闭则不再创建新的但可以激活对应的输入框 if (_tmpInputField ! null) _tmpInputField.ActivateInputField(); else if (_legacyInputField ! null) _legacyInputField.ActivateInputField(); return; }全局点击关闭键盘监听Canvas的点击事件通过GraphicRaycaster和IPointerClickHandler当点击位置不在任何输入框上时关闭当前激活的键盘和输入框。这需要另一个全局管理脚本来协调所有AutoShowKeyboard实例。5.3 键盘类型与输入约束的智能匹配我们可以扩展脚本使其能根据InputField的ContentType自动设置更合适的TouchScreenKeyboardType而不是全部用Default。private void DetermineKeyboardType() { if (_tmpInputField ! null) { switch (_tmpInputField.contentType) { case TMP_InputField.ContentType.IntegerNumber: case TMP_InputField.ContentType.DecimalNumber: keyboardType TouchScreenKeyboardType.NumberPad; break; case TMP_InputField.ContentType.EmailAddress: keyboardType TouchScreenKeyboardType.EmailAddress; break; case TMP_InputField.ContentType.PhoneNumber: keyboardType TouchScreenKeyboardType.PhonePad; break; case TMP_InputField.ContentType.Password: keyboardType TouchScreenKeyboardType.Default; secure true; break; default: keyboardType TouchScreenKeyboardType.Default; break; } } // ... 同样处理_legacyInputField ... } // 在ShowKeyboard中调用DetermineKeyboardType()6. 常见问题排查与实战技巧在实际集成过程中你可能会遇到一些“坑”。这里记录了几个典型问题及其解决方案。6.1 键盘弹出后InputField未获得焦点/光标不闪烁问题现象点击后键盘弹出了但InputField里没有闪烁的光标或者再次点击输入框时光标才出现。原因分析TouchScreenKeyboard.Open()和InputField.ActivateInputField()的调用时机或顺序可能有问题或者在UI事件链中焦点被其他元素抢走了。解决方案确保在调用Open之前或之后立即调用ActivateInputField。检查InputField的Interactable属性是否为true。检查是否有其他脚本如全局的UI管理器在错误地清除选中状态。可以尝试在OnPointerDown方法中调用eventData.Use()来阻止事件继续冒泡防止其他逻辑干扰。对于TMP_InputField可以尝试在激活后手动调用_tmpInputField.caretPosition _tmpInputField.text.Length;。6.2 在ScrollRect中点击无效或响应迟钝问题现象位于可滚动区域内的InputField有时需要点击多次才能触发键盘。原因分析ScrollRect本身也处理拖拽事件它可能会“吃掉”最初的点击事件用于判断是打算滚动还是点击。UGUI的事件系统存在优先级和冲突。解决方案调整ScrollRect的惯性Inertia在Inspector中取消勾选ScrollRect组件的Inertia。这能减少事件判断的延迟但会损失滚动的平滑感。使用EventTrigger的替代方案不为InputField添加EventTrigger而是为整个ScrollRect Content下的每个可交互子物体包括InputField添加一个透明的Image组件作为射线捕获区并在该Image上挂载事件处理脚本。这种方式更稳定但增加了Draw Call。代码控制在AutoShowKeyboard脚本的OnPointerDown方法中立即调用EventSystem.current.SetSelectedGameObject(null)然后再设置为自己有时能强制刷新焦点状态。6.3 安卓/iOS平台行为不一致问题现象在Android上运行良好在iOS上键盘可能不弹出或者弹出后立即关闭。原因分析不同平台对TouchScreenKeyboard的实现和系统调用有细微差别。iOS对UI响应的同步性要求可能更高。解决方案使用协程延迟激活将ActivateInputField()的调用包裹在一个yield return null的协程中确保它在下一帧执行避免与键盘打开调用冲突。IEnumerator ShowKeyboardCoroutine() { // 激活输入框 if (_tmpInputField ! null) _tmpInputField.ActivateInputField(); yield return null; // 等待一帧 // 再打开键盘 _keyboard TouchScreenKeyboard.Open(...); StartCoroutine(MonitorKeyboard()); }检查系统权限确保iOS项目的Info.plist中包含了必要的权限描述如NSCameraUsageDescription虽然和键盘无关但某些系统交互需要权限上下文这通常不是键盘问题的直接原因但一个干净的配置有助于排除干扰。真机测试Unity Editor中的键盘模拟和真机行为差异很大所有涉及键盘的功能必须在真机上充分测试。6.4 输入框内容类型与键盘类型不匹配问题现象一个用于输入数字的InputField弹出了全键盘而不是数字键盘。原因分析没有根据InputField的ContentType正确设置TouchScreenKeyboardType。解决方案如前文“高级功能”所述实现一个DetermineKeyboardType()方法在唤出键盘前根据contentType自动设置keyboardType。并在Inspector中保留手动覆盖的选项以备特殊情况。6.5 性能优化与对象管理问题每个InputField都运行一个MonitorKeyboard协程如果界面中有很多输入框可能会创建大量协程实例。优化可以考虑使用单例模式或静态管理器来统一管理键盘状态。所有AutoShowKeyboard脚本在唤出键盘时都通知管理器由管理器来运行唯一的监控协程并负责文本同步。这样可以减少协程数量更集中地控制逻辑。7. 完整脚本参考与预制件制作为了方便复用我们将完整的AutoShowKeyboard脚本整理如下并介绍如何制作成Prefab。7.1 完整脚本代码using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using TMPro; using System.Collections; [RequireComponent(typeof(EventTrigger))] public class AutoShowKeyboard : MonoBehaviour, IPointerDownHandler { private TMP_InputField _tmpInputField; private InputField _legacyInputField; private TouchScreenKeyboard _keyboard; [Header(Keyboard Settings)] public TouchScreenKeyboardType keyboardType TouchScreenKeyboardType.Default; public bool autocorrection true; public bool secure false; public bool selectAllOnShow false; [Tooltip(是否根据InputField的ContentType自动设置键盘类型)] public bool autoMatchKeyboardType true; void Awake() { _tmpInputField GetComponentTMP_InputField(); if (_tmpInputField null) { _legacyInputField GetComponentInputField(); } if (_tmpInputField null _legacyInputField null) { Debug.LogError(${gameObject.name} 上未找到InputField组件AutoShowKeyboard脚本将失效。); } } public void OnPointerDown(PointerEventData eventData) { ShowKeyboard(); } public void ShowKeyboard() { // 防止重复打开 if (_keyboard ! null (_keyboard.status TouchScreenKeyboard.Status.Visible || _keyboard.status TouchScreenKeyboard.Status.Done)) { // 如果键盘正在显示或刚刚完成只激活输入框 ActivateInputFieldOnly(); return; } if (!ActivateInputFieldOnly()) { return; } if (autoMatchKeyboardType) { DetermineKeyboardType(); } string initialText GetInputFieldText(); _keyboard TouchScreenKeyboard.Open(initialText, keyboardType, autocorrection, false, secure, false, ); if (_keyboard ! null) { StartCoroutine(MonitorKeyboard()); StartCoroutine(AdjustViewCoroutine()); } } private bool ActivateInputFieldOnly() { bool activated false; if (_tmpInputField ! null _tmpInputField.interactable) { _tmpInputField.ActivateInputField(); if (selectAllOnShow) _tmpInputField.SelectAll(); activated true; } else if (_legacyInputField ! null _legacyInputField.interactable) { _legacyInputField.ActivateInputField(); if (selectAllOnShow) _legacyInputField.SelectAll(); activated true; } return activated; } private void DetermineKeyboardType() { if (_tmpInputField ! null) { switch (_tmpInputField.contentType) { case TMP_InputField.ContentType.IntegerNumber: case TMP_InputField.ContentType.DecimalNumber: keyboardType TouchScreenKeyboardType.NumberPad; break; case TMP_InputField.ContentType.EmailAddress: keyboardType TouchScreenKeyboardType.EmailAddress; break; case TMP_InputField.ContentType.PhoneNumber: keyboardType TouchScreenKeyboardType.PhonePad; break; case TMP_InputField.ContentType.Password: keyboardType TouchScreenKeyboardType.Default; secure true; break; default: break; } } else if (_legacyInputField ! null) { switch (_legacyInputField.contentType) { case InputField.ContentType.IntegerNumber: case InputField.ContentType.DecimalNumber: keyboardType TouchScreenKeyboardType.NumberPad; break; case InputField.ContentType.EmailAddress: keyboardType TouchScreenKeyboardType.EmailAddress; break; case InputField.ContentType.PhoneNumber: keyboardType TouchScreenKeyboardType.PhonePad; break; case InputField.ContentType.Password: keyboardType TouchScreenKeyboardType.Default; secure true; break; default: break; } } } private string GetInputFieldText() { if (_tmpInputField ! null) return _tmpInputField.text; if (_legacyInputField ! null) return _legacyInputField.text; return ; } private IEnumerator MonitorKeyboard() { if (_keyboard null) yield break; while (_keyboard ! null _keyboard.status TouchScreenKeyboard.Status.Visible) { SyncTextFromKeyboard(); yield return null; } // 键盘状态不再是VisibleDone, Canceled, LostFocus OnKeyboardClosed(); } private void SyncTextFromKeyboard() { if (_keyboard null) return; string kText _keyboard.text; if (_tmpInputField ! null _tmpInputField.text ! kText) { _tmpInputField.text kText; // 保持光标在末尾 _tmpInputField.caretPosition kText.Length; } else if (_legacyInputField ! null _legacyInputField.text ! kText) { _legacyInputField.text kText; } } private void OnKeyboardClosed() { SyncTextFromKeyboard(); DeactivateInputField(); _keyboard null; } private void DeactivateInputField() { if (_tmpInputField ! null) _tmpInputField.DeactivateInputField(); else if (_legacyInputField ! null) _legacyInputField.DeactivateInputField(); } private IEnumerator AdjustViewCoroutine() { yield return null; // 等待一帧让键盘布局生效 // 这里可以调用独立的视图调整方法 // AdjustViewport(); } // 可选当脚本被禁用或物体被销毁时关闭键盘 void OnDisable() { if (_keyboard ! null _keyboard.status TouchScreenKeyboard.Status.Visible) { _keyboard.active false; } StopAllCoroutines(); } }7.2 制作可复用的预制件在场景中配置好一个带有TMP_InputField或InputField和EventTrigger的GameObject。将上面的AutoShowKeyboard脚本挂载上去。在EventTrigger组件中添加对PointerDown事件的监听并拖拽自身GameObject到Object栏选择AutoShowKeyboard.ShowKeyboard方法。将这个配置好的GameObject从Hierarchy拖到Project窗口创建一个Prefab。以后在任何需要此功能的地方直接实例化这个Prefab即可无需重复配置EventTrigger。一个重要的实操心得对于大型项目我倾向于创建一个KeyboardInputField的完整预制件它包含RectTransform、CanvasRenderer、Image作为背景、TMP_InputField、EventTrigger和AutoShowKeyboard脚本。然后通过编辑器脚本批量替换项目中原有的普通InputField这样可以极大地提升开发效率并保证所有输入框行为一致。