重写了EditText的setText()后报String cannot be cast to android.text.Editable错误

2019-08-13

关键字:自定义EditText、java.lang.ClassCastException: java.lang.String cannot be cast to android.text.Editable


错误发生在继承自官方 EditText 实现自定义视图的场景下。当重写了父类中的 

public void setText(CharSequence text, BufferType type)

方法时就报了异常,异常堆栈信息如下:

08-13 02:34:31.342 5859-5859/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.my.pkg, PID: 5859
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.my.pkg/com.my.pkg.MainActivity}: android.view.InflateException: Binary XML file line #10: Error inflating class com.my.pkg.IPEditText
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2195)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
        at android.app.ActivityThread.access$800(ActivityThread.java:135)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:136)
        at android.app.ActivityThread.main(ActivityThread.java:5017)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
        at dalvik.system.NativeStart.main(Native Method)
     Caused by: android.view.InflateException: Binary XML file line #10: Error inflating class com.my.pkg.IPEditText
        at android.view.LayoutInflater.createView(LayoutInflater.java:621)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:697)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:756)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
        at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:290)
        at android.app.Activity.setContentView(Activity.java:1929)
        at com.my.pkg.MainActivity.onCreate(MainActivity.java:21)
        at android.app.Activity.performCreate(Activity.java:5231)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245) 
        at android.app.ActivityThread.access$800(ActivityThread.java:135) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:136) 
        at android.app.ActivityThread.main(ActivityThread.java:5017) 
        at java.lang.reflect.Method.invokeNative(Native Method) 
        at java.lang.reflect.Method.invoke(Method.java:515) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595) 
        at dalvik.system.NativeStart.main(Native Method) 
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Constructor.constructNative(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at android.view.LayoutInflater.createView(LayoutInflater.java:595)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:697) 
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:756) 
        at android.view.LayoutInflater.inflate(LayoutInflater.java:492) 
        at android.view.LayoutInflater.inflate(LayoutInflater.java:397) 
        at android.view.LayoutInflater.inflate(LayoutInflater.java:353) 
        at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:290) 
        at android.app.Activity.setContentView(Activity.java:1929) 
        at com.my.pkg.MainActivity.onCreate(MainActivity.java:21) 
        at android.app.Activity.performCreate(Activity.java:5231) 
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087) 
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159) 
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245) 
        at android.app.ActivityThread.access$800(ActivityThread.java:135) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:136) 
        at android.app.ActivityThread.main(ActivityThread.java:5017) 
        at java.lang.reflect.Method.invokeNative(Native Method) 
        at java.lang.reflect.Method.invoke(Method.java:515) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595) 
        at dalvik.system.NativeStart.main(Native Method) 
     Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to android.text.Editable
        at android.widget.EditText.getText(EditText.java:75)
        at com.my.pkg.IPEditText.textInitialization(IPEditText.java:55)
        at com.my.pkg.IPEditText.<init>(IPEditText.java:43)
        at java.lang.reflect.Constructor.constructNative(Native Method) 
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423) 
        at android.view.LayoutInflater.createView(LayoutInflater.java:595) 
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:697) 
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:756) 
        at android.view.LayoutInflater.inflate(LayoutInflater.java:492) 
        at android.view.LayoutInflater.inflate(LayoutInflater.java:397) 
        at android.view.LayoutInflater.inflate(LayoutInflater.java:353) 
        at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:290) 
        at android.app.Activity.setContentView(Activity.java:1929) 
        at com.my.pkg.MainActivity.onCreate(MainActivity.java:21) 
        at android.app.Activity.performCreate(Activity.java:5231) 
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087) 
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159) 
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245) 
        at android.app.ActivityThread.access$800(ActivityThread.java:135) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:136) 
        at android.app.ActivityThread.main(ActivityThread.java:5017) 
        at java.lang.reflect.Method.invokeNative(Native Method) 
        at java.lang.reflect.Method.invoke(Method.java:515) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595) 
        at dalvik.system.NativeStart.main(Native Method) 

这份异常的关键在于最后标红加粗部分。它描述的是遇到了类型转换异常,String 不能被强转成 Editable。

到底怎么回事呢?

原因是在重写了父类的 setText(CharSequence, BufferType) 后没有调用父类中的这个方法,即:

    @Override
    public void setText(CharSequence text, BufferType type) {
//        super.setText(text, type);
    }

那为什么不执行父类的这个方法,竟然会导致类型转换异常呢?

这个得去跟踪一下 TextView 的源码,即 EditText 的父类,EditText 虽然继承自 TextView,但它并没有多少自己的逻辑,主要还是靠 TextView。

在 TextView 中,用于记录显示的文本的是 mText 变量。

private CharSequence mText;

这个变量的值默认是 null,在构造方法中被指向一个空的 String 常量:

在 TextView 的构造方法中,会去读取 xml 中配置在 android:text 属性中的字符串值:

随后会调用 setText 方法来为 mText 赋值,将 xml 中的文本值赋给 mText 变量。

前面的 text 变量本身就是 CharSequence 类型的,默认的 setText 方法里面也并没有什么什么特别的操作,就是简单地将 text 的值赋给了 mText 而已,但是在这里有一个很重要的性质的改变,就是原本的指向普通空值 String 的 mText 在经过了 setText 以后变成了指向一个 CharSequence 类型对象。

这一性质转换本身并不会引发什么,但是 EditText 的加载过程中会去调用 getText() 方法。EditText 方法中的 getText() 方法的实现很短,如下所示:

    @Override
    public Editable getText() {
        return (Editable) super.getText();
    }

它会去向父类,即 TextView,通过 getText() 方法要来值,再不由分说强转成 Editable 返回。TextView 中的 getText() 的实现是什么?就是简单的将 mText 对象返回嘛:

    public CharSequence getText() {
        return mText;
    }

到这,就足够清晰了吧?

如果没有执行父类的 setText(CharSequence,BufferType) 方法,那么,mText 就默认指向一个空值 String 类对象。在 EditText 的加载过程中会去调用 getText() 方法,这样一来,就要强制将 String 类型转换成 Editable 类型了,这种转换是不正确的。

那怎么解决呢?我就是不想调用父类的 setText(CharSequence,BufferType) 方法,能避免这个错误吗?

我不知道啊。mText 是 private 修饰的,不能直接更改。但是感觉可以尝试一下用反射的方式来解决,给 mText 一个正儿八经的 CharSequence 类对象就可以了嘛。如果你实在有兴趣,可以仔细去研读一下 TextView 的源码以寻求一个解决方案了,我反正是没兴趣。


原文地址:https://www.cnblogs.com/chorm590/p/11337897.html