问题
My app includes a custom IME, MyCustomIME. It works fine when the user manually selects it as the device's input method.
However, when I try to specify MyCustomIME as the input method for an EditText within my EnterPinActivity's layout file:
<EditText
android:id="@+id/pinEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberPassword"
android:inputMethod="com.example.MyCustomIME"
android:maxLength="4" />
I get this exception:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example/com.example.EnterPinActivity}: android.view.InflateException: Binary XML file line #35: Error inflating class EditText
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3150)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3260)
at android.app.ActivityThread.access$1000(ActivityThread.java:218)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1734)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6934)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
Caused by: android.view.InflateException: Binary XML file line #35: Error inflating class EditText
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:770)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:813)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:821)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:821)
at android.view.LayoutInflater.inflate(LayoutInflater.java:511)
at android.view.LayoutInflater.inflate(LayoutInflater.java:415)
at android.view.LayoutInflater.inflate(LayoutInflater.java:366)
at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:555)
at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:161)
at com.example.BaseActivity.setContentView(BaseActivity.java:365)
at com.example.EnterPinActivity.onCreate(EnterPinActivity.java:37)
at android.app.Activity.performCreate(Activity.java:6609)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1134)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3103)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3260)
at android.app.ActivityThread.access$1000(ActivityThread.java:218)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1734)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6934)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: com.example.MyCustomIME
at android.widget.TextView.<init>(TextView.java:1616)
at android.widget.EditText.<init>(EditText.java:88)
at android.widget.EditText.<init>(EditText.java:84)
at androidx.appcompat.widget.AppCompatEditText.<init>(AppCompatEditText.java:73)
at androidx.appcompat.widget.AppCompatEditText.<init>(AppCompatEditText.java:69)
at androidx.appcompat.app.AppCompatViewInflater.createEditText(AppCompatViewInflater.java:197)
at androidx.appcompat.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:115)
at androidx.appcompat.app.AppCompatDelegateImpl.createView(AppCompatDelegateImpl.java:1407)
at androidx.appcompat.app.AppCompatDelegateImpl.onCreateView(AppCompatDelegateImpl.java:1457)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:732)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:813)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:821)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:821)
at android.view.LayoutInflater.inflate(LayoutInflater.java:511)
at android.view.LayoutInflater.inflate(LayoutInflater.java:415)
at android.view.LayoutInflater.inflate(LayoutInflater.java:366)
at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:555)
at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:161)
at com.example.BaseActivity.setContentView(BaseActivity.java:365)
at com.example.EnterPinActivity.onCreate(EnterPinActivity.java:37)
at android.app.Activity.performCreate(Activity.java:6609)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1134)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3103)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3260)
at android.app.ActivityThread.access$1000(ActivityThread.java:218)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1734)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6934)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
Caused by: java.lang.ClassNotFoundException: com.example.MyCustomIME
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:309)
at java.lang.Class.forName(Class.java:273)
at android.widget.TextView.<init>(TextView.java:1614)
at android.widget.EditText.<init>(EditText.java:88)
at android.widget.EditText.<init>(EditText.java:84)
at androidx.appcompat.widget.AppCompatEditText.<init>(AppCompatEditText.java:73)
at androidx.appcompat.widget.AppCompatEditText.<init>(AppCompatEditText.java:69)
at androidx.appcompat.app.AppCompatViewInflater.createEditText(AppCompatViewInflater.java:197)
at androidx.appcompat.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:115)
at androidx.appcompat.app.AppCompatDelegateImpl.createView(AppCompatDelegateImpl.java:1407)
at androidx.appcompat.app.AppCompatDelegateImpl.onCreateView(AppCompatDelegateImpl.java:1457)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:732)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:813)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:821)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:821)
at android.view.LayoutInflater.inflate(LayoutInflater.java:511)
at android.view.LayoutInflater.inflate(LayoutInflater.java:415)
at android.view.LayoutInflater.inflate(LayoutInflater.java:366)
at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:555)
at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:161)
at com.example.BaseActivity.setContentView(BaseActivity.java:365)
at com.example.EnterPinActivity.onCreate(EnterPinActivity.java:37)
at android.app.Activity.performCreate(Activity.java:6609)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1134)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3103)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3260)
at android.app.ActivityThread.access$1000(ActivityThread.java:218)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1734)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6934)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.example.MyCustomIME" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:309)
at java.lang.Class.forName(Class.java:273)
at android.widget.TextView.<init>(TextView.java:1614)
at android.widget.EditText.<init>(EditText.java:88)
at android.widget.EditText.<init>(EditText.java:84)
at androidx.appcompat.widget.AppCompatEditText.<init>(AppCompatEditText.java:73)
at androidx.appcompat.widget.AppCompatEditText.<init>(AppCompatEditText.java:69)
at androidx.appcompat.app.AppCompatViewInflater.createEditText(AppCompatViewInflater.java:197)
at androidx.appcompat.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:115)
at androidx.appcompat.app.AppCompatDelegateImpl.createView(AppCompatDelegateImpl.java:1407)
at androidx.appcompat.app.AppCompatDelegateImpl.onCreateView(AppCompatDelegateImpl.java:1457)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:732)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:813)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:821)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:821)
at android.view.LayoutInflater.inflate(LayoutInflater.java:511)
at android.view.LayoutInflater.inflate(LayoutInflater.java:415)
at android.view.LayoutInflater.inflate(LayoutInflater.java:366)
at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:555)
at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:161)
at com.example.BaseActivity.setContentView(BaseActivity.java:365)
at com.example.EnterPinActivity.onCreate(EnterPinActivity.java:37)
at android.app.Activity.performCreate(Activity.java:6609)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1134)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3103)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3260)
at android.app.ActivityThread.access$1000(ActivityThread.java:218)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1734)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6934)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
Suppressed: java.lang.ClassNotFoundException: com.example.MyCustomIME
at java.lang.Class.classForName(Native Method)
at java.lang.BootClassLoader.findClass(ClassLoader.java:
This is how MyCustomIME appears in my manifest:
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example">
...
<application
android:name=".MyApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher_2"
android:label="@string/app_name"
android:largeHeap="true"
android:supportsRtl="true"
android:theme="@style/AppTheme">
...
<service
android:name=".MyCustomIME"
android:label="@string/my_custom_keyboard"
android:permission="android.permission.BIND_INPUT_METHOD">
<intent-filter>
<action
android:name="android.view.InputMethod" />
</intent-filter>
<meta-data
android:name="android.view.im"
android:resource="@xml/keyboard_method" />
</service>
...
</application>
</manifest>
MyApplication extends MultiDexApplication. And, for completeness, this is the MyCustomIME class:
package com.example;
import android.annotation.SuppressLint;
import android.content.Context;
import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.Keyboard.Key;
import android.inputmethodservice.KeyboardView;
import android.media.AudioManager;
import android.os.IBinder;
import android.text.InputType;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import java.util.List;
public class MyCustomIME extends InputMethodService implements KeyboardView.OnKeyboardActionListener {
private static final String TAG = MyCustomIME.class.getSimpleName();
private static final String MODE_DECIMAL = "decimal";
private static final String MODE_NUMERIC = "numeric";
private static final String MODE_QWERTY = "qwerty";
private static final int KEY_CODE_SWITCH_KEYBOARD = 1111;
private static final int KEY_CODE_BACKSPACE = -5;
private KeyboardView keyboardView;
private Keyboard keyboard;
private boolean caps = false;
private boolean capWords = false;
@Override @SuppressLint("InflateParams")
public View onCreateInputView() {
Log.d(TAG, "onCreateInputView()");
keyboardView = (KeyboardView)getLayoutInflater().inflate(R.layout.keyboard, null);
InputMethodSubtype currentInputMethodSubtype = getCurrentInputMethodSubtype();
if (currentInputMethodSubtype != null) {
mySetKeyboard(currentInputMethodSubtype);
}
return keyboardView;
}
private void mySetKeyboard(InputMethodSubtype inputMethodSubtype) {
String mode = inputMethodSubtype.getMode();
if (mode.equals(MODE_DECIMAL)) {
keyboard = new Keyboard(this, R.xml.keyboard_decimal);
}
else if (mode.equals(MODE_NUMERIC)) {
keyboard = new Keyboard(this, R.xml.keyboard_numeric);
}
else {
keyboard = new Keyboard(this, R.xml.keyboard_qwerty);
}
keyboard.setShifted(caps);
InputMethodManager imeManager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imeManager != null) {
IBinder imeToken = getImeToken();
boolean shouldOfferSwitchingToNextInputMethod = imeManager.shouldOfferSwitchingToNextInputMethod(imeToken);
if (!shouldOfferSwitchingToNextInputMethod) {
// Hide the switch key
int switchKeyWidth = 0;
List<Key> keys = keyboard.getKeys();
for (Key key : keys) {
if (key.codes[0] == KEY_CODE_SWITCH_KEYBOARD) {
switchKeyWidth = key.width;
key.width = 0;
key.icon = null;
key.iconPreview = null;
break;
}
}
// Make the BACKSPACE button bigger
for (Key key : keys) {
if (key.codes[0] == KEY_CODE_BACKSPACE) {
key.width += switchKeyWidth;// + key.gap;
key.edgeFlags = Keyboard.EDGE_RIGHT;
break;
}
}
}
}
keyboardView.setKeyboard(keyboard);
keyboardView.setOnKeyboardActionListener(this);
}
private InputMethodSubtype getCurrentInputMethodSubtype() {
InputMethodManager imeManager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imeManager != null) {
return imeManager.getCurrentInputMethodSubtype();
}
else {
return null;
}
}
private IBinder getImeToken() {
Window window = getWindow().getWindow();
if (window != null) {
return window.getAttributes().token;
}
else {
return null;
}
}
private void setInputMethodAndSubtype(String inputMethodId, InputMethodSubtype inputMethodSubtype) {
IBinder imeToken = getImeToken();
if (imeToken != null) {
InputMethodManager imeManager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imeManager != null) {
imeManager.setInputMethodAndSubtype(imeToken, inputMethodId, inputMethodSubtype);
}
else {
Log.e(TAG, "Could not set setInputMethodAndSubtype as imeManager == null");
Utilities.makeToast(this, "Could not set setInputMethodAndSubtype as imeManager == null");
}
}
else {
Log.e(TAG, "Could not set setInputMethodAndSubtype as imeToken == null");
Utilities.makeToast(this, "Could not set setInputMethodAndSubtype as imeToken == null");
}
}
@Override
public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newInputMethodSubtype) {
if (keyboardView != null) {
mySetKeyboard(newInputMethodSubtype);
}
else {
Log.e(TAG, "Could not setKeyboard as keyboardView == null");
Utilities.makeToast(this, "Could not setKeyboard as keyboardView == null");
}
//super.onCurrentInputMethodSubtypeChanged(newInputMethodSubtype);
}
@Override
public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
int editTextInputType = editorInfo.inputType;
Log.d(TAG, "editTextInputType: " + editTextInputType);
int inputTypeMaskClass = editTextInputType & InputType.TYPE_MASK_CLASS;
boolean isEditTextInputTypeNumber = inputTypeMaskClass == InputType.TYPE_CLASS_NUMBER;
int inputTypeMaskFlags = editTextInputType & InputType.TYPE_MASK_FLAGS;
boolean isEditTextInputTypeDecimal = inputTypeMaskFlags == InputType.TYPE_NUMBER_FLAG_DECIMAL;
//caps = editorInfo.initialCapsMode != 0;
caps = inputTypeMaskFlags == InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS
// || inputTypeMaskFlags == InputType.TYPE_TEXT_FLAG_CAP_WORDS
// || inputTypeMaskFlags == InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
;
capWords = inputTypeMaskFlags == InputType.TYPE_TEXT_FLAG_CAP_WORDS;
InputMethodManager imeManager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imeManager != null) {
final List<InputMethodInfo> enabledImis = imeManager.getEnabledInputMethodList();
for (InputMethodInfo imi : enabledImis) {
String serviceName = imi.getServiceName();
Log.d(TAG, "serviceName: " + serviceName);
String imiId = imi.getId();
Log.d(TAG, "imiId: " + imiId);
if (serviceName.endsWith(TAG)) { // If it's our custom keyboard
List<InputMethodSubtype> inputMethodSubtypes = imeManager.getEnabledInputMethodSubtypeList(imi,
true // allowsImplicitlySelectedSubtypes
);
// For each of our "qwerty", "numeric" and "decimal" subTypes.
for (InputMethodSubtype inputMethodSubtype : inputMethodSubtypes) {
String keyboardMode = inputMethodSubtype.getMode();
if (isEditTextInputTypeNumber) {
if (isEditTextInputTypeDecimal) {
if (keyboardMode.equals(MODE_DECIMAL)) {
setInputMethodAndSubtype(imiId, inputMethodSubtype);
break;
}
}
else {
if (keyboardMode.equals(MODE_NUMERIC)) {
setInputMethodAndSubtype(imiId, inputMethodSubtype);
break;
}
}
}
else {
if (keyboardMode.equals(MODE_QWERTY)) {
setInputMethodAndSubtype(imiId, inputMethodSubtype);
break;
}
}
}
}
}
}
super.onStartInputView(editorInfo, restarting);
}
private void playClick(int keyCode){
AudioManager audioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
if (audioManager != null) {
switch (keyCode) {
case 32:
audioManager.playSoundEffect(AudioManager.FX_KEYPRESS_SPACEBAR);
break;
case Keyboard.KEYCODE_DONE:
case 10:
audioManager.playSoundEffect(AudioManager.FX_KEYPRESS_RETURN);
break;
case Keyboard.KEYCODE_DELETE:
audioManager.playSoundEffect(AudioManager.FX_KEYPRESS_DELETE);
break;
default:
audioManager.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD);
}
}
}
@Override
public void onKey(int primaryCode, int[] keyCodes) {
InputConnection inputConnection = getCurrentInputConnection();
playClick(primaryCode);
switch(primaryCode){
case Keyboard.KEYCODE_DELETE:
inputConnection.deleteSurroundingText(1, 0);
break;
case Keyboard.KEYCODE_SHIFT:
caps = !caps;
Log.d(TAG, "2. caps set to " + caps);
keyboard.setShifted(caps);
keyboardView.invalidateAllKeys();
break;
case Keyboard.KEYCODE_DONE:
inputConnection.performEditorAction(EditorInfo.IME_ACTION_DONE);
break;
case KEY_CODE_SWITCH_KEYBOARD:
InputMethodManager imeManager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imeManager != null) {
imeManager.switchToNextInputMethod(getImeToken(),
true // onlyCurrentIme
);
}
break;
default:
char code = (char)primaryCode;
if(Character.isLetter(code) && caps){
code = Character.toUpperCase(code);
}
inputConnection.commitText(String.valueOf(code), 1);
}
}
@Override
public void onText(CharSequence text) {
}
@Override
public void onPress(int primaryCode) {
}
@Override
public void onRelease(int primaryCode) {
}
@Override
public void swipeDown() {
}
@Override
public void swipeLeft() {
}
@Override
public void swipeRight() {
}
@Override
public void swipeUp() {
}
}
What I've tried:
- Cleaning the project
- Turning off proguard
- Renaming the MyCustomIME to MyCustomIME2
- Invalidate Caches / Restart
- Uninstall and re-install app
but the problem remains. Any ideas?
来源:https://stackoverflow.com/questions/59410616/classnotfoundexception-when-specifying-custom-ime-as-edittexts-androidinputmet