UIInput有个字段是"Character Limit",它决定了输入字符数目的上限。每个字符不论是中文还是英文,都算1个字符。
有个很常见的需求是根据字节数限制输入长度,英文数字算1个字节,中文算2个字节。我们需要修改UIInput以实现该功能,网上有相关的处理代码,可惜随着NGUI更新已经过时,我只好重新写了一个修改实现,并分享出来。因为整个文件过长,本文只提到修改的地方。
打开文件 UIInput,首先添加变量 byteLimit,它就是我们需要配置的字节数。把它放到 characterLimit 的下面
public int byteLimit = 0;
接下来添加计算字节数的方法,注意用UTF-8格式不能直接得到字节数
// 获得字符串的字节数,中文2字节,英文1字节
// 用UTF8格式(中文3字符,英文1字符)迂回计算长度,再计算得到正确结果
protected int GetStringByteLength(string str)
{
return (Encoding.UTF8.GetByteCount(str) + str.Length) / 2;
}
我们可以查看characterLimit在哪里被调用,在这些地方也处理byteLimit,首先是Validate()方法,修改如下
public string Validate (string val)
{
if (string.IsNullOrEmpty(val)) return "";
StringBuilder sb = new StringBuilder(val.Length);
for (int i = 0; i < val.Length; ++i)
{
char c = val[i];
if (onValidate != null) c = onValidate(sb.ToString(), sb.Length, c);
else if (validation != Validation.None) c = Validate(sb.ToString(), sb.Length, c);
if (c != 0) sb.Append(c);
}
string result = null;
if (characterLimit > 0 && sb.Length > characterLimit)
result = sb.ToString(0, characterLimit);
// 检查 ByteLimit
if (byteLimit > 0)
{
result = result ?? sb.ToString();
if (result.Length == 0) // 空串直接放行
{
return result;
}
if (GetStringByteLength(result.Substring(0, 1)) > byteLimit) // 如果第一个字符就超了,直接结束
{
return "";
}
if (GetStringByteLength(result) <= byteLimit) // 如果最后一个字节也没超,直接放行
{
return result;
}
// 用二分法找到超和不超的边界位置
int l = 0;
int r = result.Length - 1;
int m = (l + r) / 2;
while (m != l)
{
if (GetStringByteLength(result.Substring(0, m + 1)) > byteLimit)
{
r = m;
}
else
{
l = m;
}
m = (l + r) / 2;
}
if (GetStringByteLength(result.Substring(0, m + 1)) > byteLimit)
{
result = result.Substring(0, m);
}
else
{
result = result.Substring(0, m + 1);
}
}
return result ?? sb.ToString();
}
再修改 Insert() 方法就完成了
protected virtual void Insert (string text)
{
string left = GetLeftText();
string right = GetRightText();
int rl = right.Length;
int lbl = GetStringByteLength(left);
int rbl = GetStringByteLength(right);
StringBuilder sb = new StringBuilder(left.Length + right.Length + text.Length);
sb.Append(left);
// Append the new text
for (int i = 0, imax = text.Length; i < imax; ++i)
{
// If we have an input validator, validate the input first
char c = text[i];
if (c == '\b')
{
DoBackspace();
continue;
}
// Can't go past the character limit
if (characterLimit > 0 && sb.Length + rl >= characterLimit) break;
// Can't go past the byte limit
if (byteLimit > 0 && GetStringByteLength(sb.ToString() + c) + rbl > byteLimit) break;
if (onValidate != null) c = onValidate(sb.ToString(), sb.Length, c);
else if (validation != Validation.None) c = Validate(sb.ToString(), sb.Length, c);
// Append the character if it hasn't been invalidated
if (c != 0) sb.Append(c);
}
// Advance the selection
mSelectionStart = sb.Length;
mSelectionEnd = mSelectionStart;
// Append the text that follows it, ensuring that it's also validated after the inserted value
for (int i = 0, imax = right.Length; i < imax; ++i)
{
char c = right[i];
if (onValidate != null) c = onValidate(sb.ToString(), sb.Length, c);
else if (validation != Validation.None) c = Validate(sb.ToString(), sb.Length, c);
if (c != 0) sb.Append(c);
}
mValue = sb.ToString();
UpdateLabel();
ExecuteOnChange();
}
要在Inspector里显示byteLimit,所以我们最后要修改UIInputEditor的OnInspectorGUI()方法
public override void OnInspectorGUI ()
{
UIInput input = target as UIInput;
serializedObject.Update();
GUILayout.Space(3f);
NGUIEditorTools.SetLabelWidth(110f);
//NGUIEditorTools.DrawProperty(serializedObject, "m_Script");
EditorGUI.BeginDisabledGroup(serializedObject.isEditingMultipleObjects);
SerializedProperty label = NGUIEditorTools.DrawProperty(serializedObject, "label");
EditorGUI.EndDisabledGroup();
EditorGUI.BeginDisabledGroup(label == null || label.objectReferenceValue == null);
{
if (Application.isPlaying) NGUIEditorTools.DrawPaddedProperty("Value", serializedObject, "mValue");
else NGUIEditorTools.DrawPaddedProperty("Starting Value", serializedObject, "mValue");
NGUIEditorTools.DrawPaddedProperty(serializedObject, "savedAs");
NGUIEditorTools.DrawProperty("Active Text Color", serializedObject, "activeTextColor");
EditorGUI.BeginDisabledGroup(serializedObject.isEditingMultipleObjects);
{
if (label != null && label.objectReferenceValue != null)
{
SerializedObject ob = new SerializedObject(label.objectReferenceValue);
ob.Update();
NGUIEditorTools.DrawProperty("Inactive Color", ob, "mColor");
ob.ApplyModifiedProperties();
}
else EditorGUILayout.ColorField("Inactive Color", Color.white);
}
EditorGUI.EndDisabledGroup();
NGUIEditorTools.DrawProperty("Caret Color", serializedObject, "caretColor");
NGUIEditorTools.DrawProperty("Selection Color", serializedObject, "selectionColor");
NGUIEditorTools.DrawPaddedProperty(serializedObject, "inputType");
NGUIEditorTools.DrawPaddedProperty(serializedObject, "validation");
NGUIEditorTools.DrawPaddedProperty("Mobile Keyboard", serializedObject, "keyboardType");
NGUIEditorTools.DrawPaddedProperty(" Hide Input", serializedObject, "hideInput");
NGUIEditorTools.DrawPaddedProperty(serializedObject, "onReturnKey");
// Deprecated, use UIKeyNavigation instead.
//NGUIEditorTools.DrawProperty(serializedObject, "selectOnTab");
SerializedProperty sp = serializedObject.FindProperty("characterLimit");
GUILayout.BeginHorizontal();
if (sp.hasMultipleDifferentValues || input.characterLimit > 0)
{
EditorGUILayout.PropertyField(sp);
NGUIEditorTools.DrawPadding();
}
else
{
EditorGUILayout.PropertyField(sp);
GUILayout.Label("unlimited");
}
GUILayout.EndHorizontal();
// Byte Limit
SerializedProperty spbl = serializedObject.FindProperty("byteLimit");
GUILayout.BeginHorizontal();
if (spbl.hasMultipleDifferentValues || input.byteLimit > 0)
{
EditorGUILayout.PropertyField(spbl);
NGUIEditorTools.DrawPadding();
}
else
{
EditorGUILayout.PropertyField(spbl);
GUILayout.Label("unlimited");
}
GUILayout.EndHorizontal();
NGUIEditorTools.SetLabelWidth(80f);
EditorGUI.BeginDisabledGroup(serializedObject.isEditingMultipleObjects);
NGUIEditorTools.DrawEvents("On Submit", input, input.onSubmit);
NGUIEditorTools.DrawEvents("On Change", input, input.onChange);
EditorGUI.EndDisabledGroup();
}
EditorGUI.EndDisabledGroup();
serializedObject.ApplyModifiedProperties();
}
把上面的代码全部替换即可