在开发触屏设备应用时,系统自带的软键盘往往存在兼容性问题。比如在Windows一体机上,不同版本的系统键盘弹出效果不一致;在工业平板等定制设备中,系统键盘可能直接被禁用。这时候就需要我们自制一个全平台通用的虚拟键盘。
我去年给某医院开发挂号机时就遇到过这种情况。Windows自带的触摸键盘会遮挡半个屏幕,而且无法自定义按钮功能。最终用UGUI做的虚拟键盘不仅完美适配了43寸触摸屏,还增加了医疗专用的"医保卡""身份证"等快捷输入按钮。
虚拟键盘的UI核心是**网格布局(Grid Layout)**组件。建议先创建一个空物体挂载Grid Layout Group组件,设置:
csharp复制// 动态调整布局的示例代码
void AdjustLayout()
{
GridLayoutGroup grid = GetComponent<GridLayoutGroup>();
float totalWidth = Screen.width * 0.9f;
float cellSize = totalWidth / 12; // 每行12个键
grid.cellSize = new Vector2(cellSize, cellSize);
}
csharp复制// 按钮预制体结构示例
KeyboardButton(Prefab)
├── Button (Image+Button组件)
│ ├── NormalColor : #FFFFFFFF
│ ├── PressedColor : #AAAAAAFF
├── Text (TextMeshPro)
采用二维数组存储键位信息是最佳实践,第一维表示行,第二维存储该行键位:
csharp复制private string[][] keyMap = {
new []{"1","2","3","4","5","6","7","8","9","0"},
new []{"q","w","e","r","t","y","u","i","o","p"},
//...其他行
};
使用对象池技术优化性能:
csharp复制List<GameObject> buttonPool = new List<GameObject>();
void CreateButton(string text)
{
GameObject btn = buttonPool.Find(x=>!x.activeSelf);
if(btn == null){
btn = Instantiate(buttonPrefab);
buttonPool.Add(btn);
}
btn.GetComponentInChildren<TMP_Text>().text = text;
btn.SetActive(true);
}
通过事件委托实现全局状态切换:
csharp复制public delegate void ShiftEvent(bool isUpper);
public static event ShiftEvent OnShiftChange;
// 切换大小写按钮点击
public void OnShiftClick()
{
isCapsLock = !isCapsLock;
OnShiftChange?.Invoke(isCapsLock);
}
// 单个按键的响应
public class KeyButton : MonoBehaviour
{
void OnEnable()
{
KeyboardManager.OnShiftChange += UpdateCase;
}
void UpdateCase(bool isUpper)
{
text.text = isUpper ? text.text.ToUpper() : text.text.ToLower();
}
}
推荐使用UnityEvent实现松耦合:
csharp复制public class KeyboardManager : MonoBehaviour
{
[System.Serializable]
public class StringEvent : UnityEvent<string> {}
public StringEvent onTextChanged;
public void AppendText(string s)
{
currentText += s;
onTextChanged.Invoke(currentText);
}
}
// 使用处
keyboard.onTextChanged.AddListener(text=>{
inputField.text = text;
});
给按钮添加触觉反馈能显著提升用户体验:
csharp复制#if UNITY_ANDROID && !UNITY_EDITOR
using UnityEngine.Android;
public void Vibrate()
{
Handheld.Vibrate();
}
#endif
为按钮添加缩放动画:
csharp复制public class ButtonAnim : MonoBehaviour
{
public float pressScale = 0.9f;
public float animSpeed = 10f;
void Update()
{
float target = isPressed ? pressScale : 1f;
transform.localScale = Vector3.Lerp(
transform.localScale,
Vector3.one * target,
Time.deltaTime * animSpeed
);
}
}
完整的项目应包含以下核心脚本:
源码中特别值得注意的几个技巧:
问题1:按钮点击无反应
检查顺序:
问题2:UI显示错乱
尝试:
问题3:移动端输入延迟
优化方案:
我在实际项目中发现,虚拟键盘最容易出现的问题是多点触控冲突。解决方案是添加以下代码:
csharp复制void Update()
{
if(Input.touchCount > 1){
return; // 屏蔽多点触控
}
}
开发这类功能时,建议先在真机上测试,模拟器的触摸响应往往不够真实。记得为按钮添加足够的点击区域(至少48x48像素),这是Material Design的标准要求。