【问题标题】:EditText .setError() not Applying my Custom Error Icon on Re-CreateEditText .setError() 没有在重新创建时应用我的自定义错误图标
【发布时间】:2013-12-16 11:45:45
【问题描述】:

我在ViewPager 中有一个Fragment

在那个片段中,我有几个 EditText,它们的 OnTextChanged 侦听器(在 onViewCreated 方法中添加)执行一些验证工作并调用函数来设置/删除错误,例如:

// Sets or removes an error message in the nameField EditText 
public void validateUsername(String message)
{
    if(message == null)
    {
        nameField.setError(null);
    }
    else
    {
        nameField.setError(message,errorIcon);
    }
}

errorIcon 是 Drawable 实例化的 onCreate,如下所示:

errorIcon = getResources().getDrawable(R.drawable.ic_wrong_input);
errorIcon.setBounds(new Rect(0, 0, errorIcon.getIntrinsicWidth(), errorIcon.getIntrinsicHeight()));

当我在 EditText 中键入内容时,验证工作正常:显示我的错误消息,带有我的自定义图标。

但是当我旋转设备时,validateUsername() 函数被调用(预期行为),else 分支中的nameField.setError(message,errorIcon) 被执行,但EditText 得到的是 Android 的默认错误图标而不是我的(是的,调试时我可以看到我的errorIcon Drawable被实例化了)。之后,如果我在那个EditText 中输入了一些无效的东西,验证方法会再次被调用,这一次我会再次得到我的自定义错误图标。如果我再次旋转设备,同样的事情:重新创建时的默认图标,输入无效内容后的自定义图标...

谁能解释这种行为?即使我明确设置了自定义图标,为什么在重新创建后由于设备旋转而出现 Android 的默认错误图标?

编辑:

我尝试在 validateUsername() 方法中实例化 Drawable,但没有成功。

编辑 2:

我想使用自定义错误图标,以便它与我的 UI 更好地融合,还因为我正在使用另一个自定义图标(绿色图标),当他们的输入有效,我希望它们彼此一致

现在我已经辞职,我让 Android 使用默认的错误图标(即我没有在 setError() 方法上指定任何 Drawable)。当输入有效时,我将 EditText 的右 Drawable 设置为我的自定义“输入有效”图标,为了保持一致性,我将其修改为与 Android 的 indicator_input_error.png 相同的大小。

不过,我不喜欢这种方法(例如,在未来的版本中,indicator_input_error.png 图标可能会有所不同),我不明白为什么在重新创建 Fragment 时我一直得到默认图标.奇怪的是,第一次重新创建 Fragment 时,如果 EditText 验证失败并且 validateUsername() 被非空消息调用,我会得到我的自定义图标。在第二次(等等)Fragment 被重新创建时,我得到了 Android 的默认图标。如果我输入了一些无效的文本,我又得到了我的自定义图标,这很奇怪。

【问题讨论】:

  • 您可以尝试的另一件事是在 nameField 上调用 requestFocus
  • 不,这也不起作用。

标签: java android android-fragments android-edittext


【解决方案1】:

真是麻烦!我遇到了同样的问题。除了内置的错误图标外,我还有一个自定义图标来表示警告,并将根据情况显示适当的图标。

当您遇到时,setError() 在您的活动中调用 onSave/onRestoreInstanceState() 时运行异常(即:当屏幕旋转时)。

事实证明,大多数(全部?)现有的 Android 小部件都会创建自己的 parcelable 状态,以在旋转期间保留它们的弹珠。这意味着许多简单的布局不需要实现它们自己的可打包/可序列化对象,就像您很可能经历过的那样。

如果您深入研究 TextView 类,您将在其 onRestoreInstanceState() 实现的末尾看到以下内容:

      if (ss.error != null) {
        final CharSequence error = ss.error;
        // Display the error later, after the first layout pass
        post(new Runnable() {
            public void run() {
                setError(error);
            }
        });

如您所见,仅检查 ss.error(在 TextView 的 onSaveInstanceState() 期间保存)是否不为空。如果是这样,它会通过调用 setError(error) 来恢复。简而言之,您的自定义图标在保存过程中没有被打包,因此它不会被小部件恢复——导致显示标准的红色错误图标。鉴于 TextView 代码的年代久远,我怀疑这是 Google 的疏忽。

可以尝试在您自己覆盖的 onRestoreInstanceState() 中重置错误文本和图标,但由于小部件正在排队其 setError() 调用(通过调用 post() ),你的直接调用会超过它,导致你的图标显示一瞬间,只有在队列处理发生后才会被覆盖。

我通过在视图队列中调用 setError() 解决了这个问题(也使用 post() )。这可能不是学术上正确的和干净的同步(因为我依赖于消息队列处理的顺序),但它解决了问题。

简而言之,在你的 onRestoreInstanceState() 中做这样的事情:

protected void onRestoreInstanceState(Bundle savedInstanceState)
{
    // Call super first, to ensure that all the widget updates get in the queue first...
    super.onRestoreInstanceState(savedInstanceState);

    View view = getWindow().getDecorView().getRootView();

    view.post(new Runnable() 
    {
        public void run() 
        {
            myEditText.setError(...);
            .
            .
            .
        }
    });
}

要消化的东西太多了。希望这会有所帮助!

【讨论】:

  • 很好的研究和很好的答案。我还没有测试它,但它非常有意义。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-25
  • 1970-01-01
  • 2012-07-04
相关资源
最近更新 更多