之前的开发中,对于 EditText
的输入错误提醒,我常使用 Dialog
进行展示,如:
相比 Toast
而言,这是一种比较合适的交互,用户可以清楚地了解到为何自己的输入不合法,而不会因 Toast
显示时间太短导致错过具体信息。
但是,有一点不好的是增加了用户的点击成本。
当然也可以通过设定 Dialog
定时自动关闭来降低点击成本,不过由于 Dialog
在显示的时候会抢夺焦点,导致下方的 Activity
无法获取输入,所以这个成本并没有实际降低。
EditText
其实为我们提供了一种比较优雅的提示方式,它可以使用浮窗对非法输入进行提示,只需一行代码,比如用户输入为空,我们可以这样写:
if (TextUtils.isEmpty(editText.getText().toString())) {
editText.setError("Content cannot be empty!");
}
在用户输入为空时,系统就会给出相应的提示:
用户可以查阅提示信息的同时,又不会增加点击成本,很友好。
用法就这么简单,但如果文章这样结束就太草率了。
我们跟踪一下 setError()
方法的源码会发现,这个方法并不来源于 EditText
,而是来源于其父类 TextView
:
@RemoteView
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
...
/**
* Sets the right-hand compound drawable of the TextView to the "error"
* icon and sets an error message that will be displayed in a popup when
* the TextView has focus. The icon and error message will be reset to
* null when any key events cause changes to the TextView's text. If the
* <code>error</code> is <code>null</code>, the error message and icon
* will be cleared.
*/
@android.view.RemotableViewMethod
public void setError(CharSequence error) {
if (error == null) {
setError(null, null);
} else {
Drawable dr = getContext().getDrawable(com.android.internal.R.drawable.indicator_input_error);
dr.setBounds(0, 0, dr.getIntrinsicWidth(), dr.getIntrinsicHeight());
setError(error, dr);
}
}
}
如果你尝试直接对 TextView
调用 setError()
方法,会看到 TextView
虽然可以展示右侧的红色感叹号,但是并无法展示传入的内容:
不过只需认真阅读 setError()
方法上的注释,你就会发现其中的端倪,仅当 TextView
拥有焦点时 Error Message 才会显示,这也是为什么 EditText
能够直接显示的原因,因为 EditText
本身就拥有焦点:
<resources>
...
<style name="Widget.EditText">
<item name="focusable">true</item>
<item name="focusableInTouchMode">true</item>
...
</style>
</resources>
也就是说,只要我们给 TextView
手动配置焦点,它也能够正常显示:
textView.setFocusableInTouchMode(true);
textView.requestFocus();
textView.setError("Error Message");
效果如下:
从注释中我们还能看到,当传入的错误信息为 null
时,错误信息和图标能够被清除。
噢对了,setError()
还提供了重载方法允许我们自行设置图标,实际上,上面的单个参数的 setError()
内部调用的也是这个重载方法:
@RemoteView
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
...
/**
* Sets the right-hand compound drawable of the TextView to the specified
* icon and sets an error message that will be displayed in a popup when
* the TextView has focus. The icon and error message will be reset to
* null when any key events cause changes to the TextView's text. The
* drawable must already have had {@link Drawable#setBounds} set on it.
* If the <code>error</code> is <code>null</code>, the error message will
* be cleared (and you should provide a <code>null</code> icon as well).
*/
public void setError(CharSequence error, Drawable icon) {
createEditorIfNeeded();
mEditor.setError(error, icon);
notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
该重载方法需要传入一个 Drawable
对象,特别注意的是,该 Drawable
对象必须要调用 setBounds()
进行配置,本质上与我们之前在『Android 为 TextView 或 EditText 添加 ICON』介绍的差不多。
效果如下:
更深入的源码就不在本文继续解析了,有兴趣可以自行查阅。