NGUI源码学习——UIInput

UIInput是NGUI的输入控件,其在PC端表现为一个文本框,而在手机端则会拉起系统的键盘。本文主要记录其基本用法和原理,以及一些重要的实现和工具方法。

【参考版本:NGUI 3.6.9】

1. 基本用法和原理

必须和一个UILabel联动使用,用于显示输入的文字。在手机上,实际上封装了对UnityEngine.TouchScreenKeyboard mKeyboard的引用用于控制操作系统的虚拟键盘,用MOBILE宏隔开(在非手机设备上用这个类会编译错误)。

UIInput在使用时一般还需要挂一个collider,以便响应UICamera的OnPress和OnDrag事件。这两个事件都是通过GetCharUnderMouse()来获取字符索引位置,并更新selectionStart和selectionEnd,没有其他逻辑。

2. 重要方法

  • Validate:验证字符串(逐字符检查)并返回处理过的字符串。若设置了onValidate,则用它来验证,否则根据validation属性(有整形、浮点数、用户名等等选项)用内置的算法来检查字符。字符不合法则跳过该字符,最后根据characterLimit属性检查限制长度。
  • Update当被选中时,才执行以下逻辑
    • 等待mKeyboard.active
    • 在input被激活时mSelectMe记录帧数,确保下面一步【打开键盘】操作只会被执行一次
    • 打开键盘:若在手机平台上,获取TouchScreenKeyboardType、InputType、当前值等并调用TouchScreenKeyboard.Open打开与操作系统相关的键盘;若不在手机平台,则开启Input.imeCompositionMode。最后return。
    • 手机上获取输入字符:手机上通过TouchScreenKeyboard.text来获取虚拟键盘中的字符,并调用Insert/DoBackspace方法【见下方具体算法】。接着再通过mKeyboard的API确定是否调用Submit方法。
    • PC上获取输入字符:一般通过Input.inputString来获取输入的字符,并调用Insert方法显示。当使用输入法时,会用到Input.compositionStringime来更新用户已键入的字符并显示出来。(例如输出“呵呵”要输入“hehe”,后者即为ime)
    • 光标动画:一个简单的时间判断,每0.5s切换一次mCaret的显示状态
  • UpdateLabel核心方法,更新label的显示,最终要显示的字符串存储在processed变量中
    • 若input值为空,则processed = selected ? "" : mDefaultText;
    • 若inputType为密码形式,则processed为相同字符数量的星号
    • 处理label为ClampContent且限制为1行的情况:在这种模式下,移动光标或选区超出范围会可以自动滚动字符(类似scrollView)。通过label.CalculateOffsetToFit(processed)计算label尺寸可以容纳下的字符偏移,再通过一些逻辑计算裁剪掉processed
    • 创建(更新)选区和光标:先代码创建一个2×2的白色纹理,赋给挂在label底下的(也是代码创建)Highlight和Caret(均挂了UITexture)。最后调用label.PrintOverlay(start, end, mCaret.geometry, mHighlight.geometry, caretColor, selectionColor);来绘制出选区和光标矩形,其中涉及到geometry添加顶点,算法逻辑较为复杂。

3. 工具方法

  • Insert(string text):先获取选取当前选区(或光标)左侧串left和右侧串right,再遍历text的每个字符,处理退格、characterLimit和validate的情况。最后还要再对right进行validate(防止插入的text破坏了后面的合法性)
  • DoBackspace():原理是Insert(""),最终所得字符串为当前selection左侧字符串+空串+右侧字符串,等价于删掉了selection所在的字符,方法非常巧妙
  • ProcessEvent(Event ev):处理各种特殊按键事件,如箭头、退格、回车等等,绝大多数都是mSelectionStart和mSelectionEnd的逻辑处理,算法较为简单

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器