35

TextView一些(但不是全部)运行 Jelly Bean ( 4.2.1) 的设备似乎缺少感叹号错误图标,该图标应该出现在EditText通过TextView.setError(CharSequence error).

在此处输入图像描述 在此处输入图像描述

Galaxy Nexus 似乎确实缺少该图标。

效果是设置的错误状态仅在具有焦点setError时才明显。EditText这使得setError(...)它的用处大大降低,因为它通常用于鼓励用户返回EditText解决问题。例如,您有一个标准登录屏幕,其中包含在用户单击提交按钮时验证的用户名和密码表单条目。除非用户单击返回该表单,否则不会显示在用户名表单上设置的验证错误消息——这是错误图标旨在鼓励他们这样做的!

测试:(可能有一个更容易访问的 EditText,但这个是非常广泛可用的)

  1. 打开设置
  2. 选择“添加帐户”(在旧设备上的“帐户和同步”中)
  3. 选择“Google”作为帐户类型
  4. 选择“现有”(在旧设备上单击“下一步”和“登录”后)
  5. 将“电子邮件”EditText留空,点击“密码”EditText

此时,“电子邮件”上设置了一个错误,EditText说明它不能为空白。在没有此问题的设备上,会显示通常的错误图标,当获得EditText焦点时,该图标会扩展为完整的错误消息。EditText在运行 Jelly Bean 的 Galaxy Nexuses 上,不显示任何图标,并且该错误仅在“电子邮件”再次获得焦点时才可见,并且此时仍然缺少图标。

这看起来像一个错误,但我想检查其他人是否可以重现它,对问题可能是什么有想法,并有一个好的解决方法。

使用setError(CharSequence error, Drawable icon)可能会解决问题,但能够在不同的 Android 版本中使用库存错误图形会很好。

4

2 回答 2

18

临时解决方案!EditTextErrorFixed.java

虽然这确实是一个 SDK 错误,但我已经设法使用反射方法让图标按预期工作。我检查了它是否适用于 4.2 和 4.2.1,并确认它适用于我更新的 Galaxy Nexus。

来源可以在这里找到。

EditTextErrorFixed屏幕截图显示,即使焦点发生变化,底部的图标仍然存在。此外,它还包含另一个修复程序,如果用户在已经为空EditText的情况下按 Delete,错误就会消失(另一个错误?)。

演示图片

为方便起见,这里是EditTextErrorFixed来源;该类可以很容易地在 XML 中使用:

package com.olegsv.showerrorfixeddemo;

import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.widget.EditText;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * EditText which addresses issues with the error icon
 * (http://stackoverflow.com/q/13756978/832776) and also the error icon
 * disappearing on pressing delete in an empty EditText
 */
public class EditTextErrorFixed extends EditText {
    public EditTextErrorFixed(Context context) {
        super(context);
    }

    public EditTextErrorFixed(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public EditTextErrorFixed(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /**
     * Don't send delete key so edit text doesn't capture it and close error
     */
    @Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        if (TextUtils.isEmpty(getText().toString()) && keyCode == KeyEvent.KEYCODE_DEL)
            return true;
        else
            return super.onKeyPreIme(keyCode, event);
    }

    /**
     * Keep track of which icon we used last
     */
    private Drawable lastErrorIcon = null;

    /**
     * Resolve an issue where the error icon is hidden under some cases in JB
     * due to a bug http://code.google.com/p/android/issues/detail?id=40417
     */
    @Override
    public void setError(CharSequence error, Drawable icon) {
        super.setError(error, icon);
        lastErrorIcon = icon;

        // if the error is not null, and we are in JB, force
        // the error to show
        if (error != null /* !isFocused() && */) {
            showErrorIconHax(icon);
        }
    }

    /**
     * In onFocusChanged() we also have to reshow the error icon as the Editor
     * hides it. Because Editor is a hidden class we need to cache the last used
     * icon and use that
     */
    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        showErrorIconHax(lastErrorIcon);
    }

    /**
     * Use reflection to force the error icon to show. Dirty but resolves the
     * issue in 4.2
     */
    private void showErrorIconHax(Drawable icon) {
        if (icon == null)
            return;

        // only for JB 4.2 and 4.2.1
        if (android.os.Build.VERSION.SDK_INT != Build.VERSION_CODES.JELLY_BEAN &&
                android.os.Build.VERSION.SDK_INT != Build.VERSION_CODES.JELLY_BEAN_MR1)
            return;

        try {
            Class<?> textview = Class.forName("android.widget.TextView");
            Field tEditor = textview.getDeclaredField("mEditor");
            tEditor.setAccessible(true);
            Class<?> editor = Class.forName("android.widget.Editor");
            Method privateShowError = editor.getDeclaredMethod("setErrorIcon", Drawable.class);
            privateShowError.setAccessible(true);
            privateShowError.invoke(tEditor.get(this), icon);
        } catch (Exception e) {
            // e.printStackTrace(); // oh well, we tried
        }
    }
}
于 2013-01-05T19:42:22.050 回答
0

我知道这里已经有解决方案。它只是我试图不惜一切代价避免在Android上的反射。如果您对反射没问题,请先尝试下面的解决方案,因为它可能会解决问题而无需子类化和反射。

Drawable d= getResources().getDrawable(R.drawable.ic_launcher);
            d.setBounds(0, 0, 
                    d.getIntrinsicWidth(), d.getIntrinsicHeight());

            et.setError("my error",d);
于 2016-03-26T17:30:08.797 回答