重写了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 的源码以寻求一个解决方案了,我反正是没兴趣。