How to capture soft keyboard input in a View?

It is actually possible to handle the key events yourself without deriving your view from TextView.

To do this, just modify your original code as follows:

1) Replace the following line in onCreateInputConnection():

outAttrs.inputType = InputType.TYPE_CLASS_TEXT;

with this one:

outAttrs.inputType = InputType.TYPE_NULL;

Per the documentation for InputType.TYPE_NULL: "This should be interpreted to mean that the target input connection is not rich, it can not process and show things like candidate text nor retrieve the current text, so the input method will need to run in a limited 'generate key events' mode."

2) Replace the following line in the same method:

BaseInputConnection fic = new BaseInputConnection(this, true);

with this one:

BaseInputConnection fic = new BaseInputConnection(this, false);

The false second argument puts the BaseInputConnection into "dummy" mode, which is also required in order for the raw key events to be sent to your view. In the BaseInputConnection code, you can find several comments such as the following: "only if dummy mode, a key event is sent for the new text and the current editable buffer cleared."

I have used this approach to have the soft keyboard send raw events to a view of mine that is derived from LinearLayout (i.e., a view not derived from TextView), and can confirm that it works.

Of course, if you didn't need to set the IME_ACTION_DONE imeOptions value to show a Done button on the keyboard, then you could just remove the onCreateInputConnection() and onCheckIsTextEditor() overrides entirely, and raw events would then be sent to your view by default, since no input connection capable of more sophisticated processing would have been defined.

But unfortunately, there does not seem to be a simple way to configure the EditorInfo attributes without overriding these methods and providing a BaseInputConnection object, and once you have done that you will have to dumb down the processing performed by that object as described above if you want to once again receive the raw key events.

WARNING: Two bugs were introduced in certain recent versions of the default LatinIME keyboard that ships with Android (Google Keyboard) that can impact keyboard event processing (as described above) when that keyboard is in use. I've devised some workarounds on the app side, with sample code, that appear to get around these problems. To view these workarounds, see the following answer:

Android - cannot capture backspace/delete press in soft. keyboard


Turns out that I did in fact need to subclass TextView and the use addTextChangedListener() to add my own implementation of TextWatcher in order to listen to soft key events. I couldn't find a way to do this with a plain old View.

One other thing, for those who will try this technique; TextView is not able to edit text by default, so if you want to make your implementation editable (instead of subclassing EditText, which I didn't want to to do), you must also make a custom InputConnection, something like the following:

 /**
 * MyInputConnection
 * BaseInputConnection configured to be editable
 */
class MyInputConnection extends BaseInputConnection {
    private SpannableStringBuilder _editable;
    TextView _textView;

    public MyInputConnection(View targetView, boolean fullEditor) {
        super(targetView, fullEditor);
        _textView = (TextView) targetView;
    }

    public Editable getEditable() {
        if (_editable == null) {
            _editable = (SpannableStringBuilder) Editable.Factory.getInstance()
            .newEditable("Placeholder");
        }
        return _editable;
    }

    public boolean commitText(CharSequence text, int newCursorPosition) {
        _editable.append(text);
        _textView.setText(text);
        return true;
    }
}

Then you override onCheckisTextEditor and onCreateInputConnection with something like the following:

 @Override
 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
     outAttrs.actionLabel = null;
     outAttrs.label = "Test text";
     outAttrs.inputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
     outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;

     return new MyInputConnection(this, true);
 }

 @Override
 public boolean onCheckIsTextEditor() {
     return true;
 }

After this, you should have a View that can listen to the soft keyboard and you can do whatever you want with the key input values.