InputField是UGUI的重要组件,可以提供文本输入功能,是与用户交互的一个重要手段。我们可以在编辑器里,为OnValueChanged和OnEndEdit两个事件添加监听,这样就可以获得用户输入的文本。本文就探究一下InputField的实现原理。
按照惯例,附上UGUI源码下载地址。
在编辑器新建一个InputField,它会附带两个子对象Placeholder和Text。
Placeholder(占位符)是当输入文字为空时显示的提示文字,Text显示的便是输入文字。
我们先看一下InputField的继承关系:
public class InputField
: Selectable,
IUpdateSelectedHandler,
IBeginDragHandler,
IDragHandler,
IEndDragHandler,
IPointerClickHandler,
ISubmitHandler,
ICanvasElement
InputField重写了OnEnable(调用时机参见Untiy3D组件小贴士(一)OnEnabled与OnDisabled)方法,方法里设置了一些变量,然后调用了m_TextComponent的RegisterDirtyVerticesCallback方法两次,添加MarkGeometryAsDirty和UpdateLabel回调,最后再调用一下UpdateLabel。m_TextComponent对应的是Text对象的Text组件,而RegisterDirtyVerticesCallback则是Graphic的方法(Text间接继承自Graphic),当Graphic需要重建Mesh(将顶点数据设为脏的)的时候,会回调方法。MarkGeometryAsDirty方法将自己注册到CanvasUpdateRegistry的图像重建序列(参考UGUI内核大探究(六)CanvasUpdateRegistry)。
/// <summary>
/// Update the visual text Text.
/// </summary>
protected void UpdateLabel()
{
if (m_TextComponent != null && m_TextComponent.font != null && !m_PreventFontCallback)
{
// TextGenerator.Populate invokes a callback that's called for anything
// that needs to be updated when the data for that font has changed.
// This makes all Text components that use that font update their vertices.
// In turn, this makes the InputField that's associated with that Text component
// update its label by calling this UpdateLabel method.
// This is a recursive call we want to prevent, since it makes the InputField
// update based on font data that didn't yet finish executing, or alternatively
// hang on infinite recursion, depending on whether the cached value is cached
// before or after the calculation.
//
// This callback also occurs when assigning text to our Text component, i.e.,
// m_TextComponent.text = processed;
m_PreventFontCallback = true;
string fullText;
if (Input.compositionString.Length > 0)
fullText = text.Substring(0, m_CaretPosition) + Input.compositionString + text.Substring(m_CaretPosition);
else
fullText = text;
string processed;
if (inputType == InputType.Password)
processed = new string(asteriskChar, fullText.Length);
else
processed = fullText;
bool isEmpty = string.IsNullOrEmpty