How to capture soft keyboard input in view mode?

I have a subclass view that appears on the keyboard when it gets "touch up" in onTouchEvent. He shows this by asking for focus, retrieving InputMethodManager, and then calling showSoftInput.

Now I need to figure out how to capture the pressed letters of the soft keyboard when they are pressed. Currently, I only get an answer when the "Next / Done" button is pressed on the soft keyboard.

Here is my class:

public class BigGrid extends View { private static final String TAG = "BigGrid"; public BigGrid(Context context) { super(context); setFocusableInTouchMode(true); // allows the keyboard to pop up on // touch down setOnKeyListener(new OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { Log.d(TAG, "onKeyListener"); if (event.getAction() == KeyEvent.ACTION_DOWN) { // Perform action on key press Log.d(TAG, "ACTION_DOWN"); return true; } return false; } }); } @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); Log.d(TAG, "onTOUCH"); if (event.getAction() == MotionEvent.ACTION_UP) { // show the keyboard so we can enter text InputMethodManager imm = (InputMethodManager) getContext() .getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(this, InputMethodManager.SHOW_FORCED); } return true; } @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { Log.d(TAG, "onCreateInputConnection"); BaseInputConnection fic = new BaseInputConnection(this, true); outAttrs.actionLabel = null; outAttrs.inputType = InputType.TYPE_CLASS_TEXT; outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT; return fic; } @Override public boolean onCheckIsTextEditor() { Log.d(TAG, "onCheckIsTextEditor"); return true; } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(R.color.grid_bg); // . // . // alot more drawing code... // . } } 

The keyboard shows, but my onKeyListener only works when I click the "Next" button on the keyboard. I need which character is used, so I can display it in the onDraw () method.

+42
android
Mar 24 '11 at 13:16
source share
4 answers

It turns out that I really needed a subclass of TextView and use addTextChangedListener () to add my own TextWatcher implementation to listen for soft-key events. I could not find a way to do this with a simple old view.

One more thing for those who try this technique; TextView cannot edit text by default, so if you want your implementation to be edited (instead of a subclass of EditText that I didn't want to do), you should also create 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 that, you should have a view that can listen to the soft keyboard, and you can do whatever you want with the input values โ€‹โ€‹of the keys.

+20
Apr 16 '11 at 5:20
source share

In fact, you can handle key events yourself without getting your view from a TextView.

To do this, simply change the source code as follows:

1) Replace the following line in onCreateInputConnection():

 outAttrs.inputType = InputType.TYPE_CLASS_TEXT; 

with this:

 outAttrs.inputType = InputType.TYPE_NULL; 

In the documentation for InputType.TYPE_NULL: "This should be interpreted as meaning that the target input connection is not rich, it cannot process and show things like candidate text, and not extract the current text, so the input method should be run in restricted mode generating key events.

2) Replace the following line with the same method:

 BaseInputConnection fic = new BaseInputConnection(this, true); 

with this:

 BaseInputConnection fic = new BaseInputConnection(this, false); 

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

I used this approach so that the soft keyboard sends raw events in my opinion, which is obtained from LinearLayout (i.e. a view not obtained from TextView), and can confirm that it works.

Of course, if you did not need to set IME_ACTION_DONE imeOptions to display the Finish button on the keyboard, you could just completely remove the overrides onCreateInputConnection() and onCheckIsTextEditor() , and then the unprocessed events are sent to your view by default, since there werenโ€™t an input connection capable of more complex processing is defined.

But, unfortunately, there is no easy way to configure EditorInfo attributes without overriding these methods and providing a BaseInputConnection object, and once you do this, you will have to disable the processing performed by this object, as described above, if you want to get raw key events again .

WARNING. In some recent versions of the LatinIME keyboard, two errors were installed by default that come with Android (Google Keyboard), which can affect the processing of keyboard events (as described above) when this keyboard is used. I developed some workarounds on the application side, with sample code that seemed to get around these problems. To view these workarounds, see the following answer:

Android - unable to capture backspace / delete, press soft. Keyboard

+37
Sep 12 '11 at 11:02
source share

According to the documentation , the View (editor) receives commands from the keyboard (IME) through InputConnection and sends commands to the keyboard through InputMethodManager .

enter image description here

I will show all the code below, but here are the steps.

1. Display the keyboard

Since the view sends a command to the keyboard, it needs to use InputMethodManager . For example, we will say that when viewing a view, it will display the keyboard (or hide it if it is already displayed).

 @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, InputMethodManager.HIDE_IMPLICIT_ONLY); } return true; } 

The view should also have setFocusableInTouchMode(true) .

2. Get keyboard input

In order for the view to receive keyboard input, it needs to override onCreateInputConnection() . This returns the InputConnection used by the Keyboard to communicate with the view.

 @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { outAttrs.inputType = InputType.TYPE_CLASS_TEXT; return new MyInputConnection(this, true); } 

outAttrs indicate which keyboard the view requests. Here we just request a plain text keyboard. Selecting TYPE_CLASS_NUMBER displays a numeric pad (if available). There are many other options. See EditorInfo .

You should return an InputConnection , which is usually a regular subclass of BaseInputConnection . In this subclass, you provide a link to your editable line to which the keyboard will update. Since SpannableStringBuilder implements an Editable interface, we will use this in our base example.

 public class MyInputConnection extends BaseInputConnection { private SpannableStringBuilder mEditable; MyInputConnection(View targetView, boolean fullEditor) { super(targetView, fullEditor); MyCustomView customView = (MyCustomView) targetView; mEditable = customView.mText; } @Override public Editable getEditable() { return mEditable; } } 

All we have done here is to provide an input connection with a link to a text variable in our user view. BaseInputConnection will take care of modifying this mText . That may be all you need to do. However, you can check the source code and see which methods say "default implementation", especially "default implementation does nothing." These are other methods that you can override depending on how your editor looks. You should also look at all the method names in the documentation . Some of them have notes for "author authors." Pay particular attention to them.

Some keyboards do not send specific input through InputConnection for any reason (for example, delete , enter, and some keys on the numeric keypad ). For them, I added OnKeyListener . Testing this setting on five different keyboards, everything seemed to work. Additional answers related to this are given here:

  • Differentiating text code with control code in Android KeyEvent
  • Need a table of key codes for android and presenter
  • Numeric type keyboard input connection

Full project code

Here is my complete example for reference.

enter image description here

MyCustomView.java

 public class MyCustomView extends View { SpannableStringBuilder mText; public MyCustomView(Context context) { this(context, null, 0); } public MyCustomView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { setFocusableInTouchMode(true); mText = new SpannableStringBuilder(); // handle key presses not handled by the InputConnection setOnKeyListener(new OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { if (event.getUnicodeChar() == 0) { // control character if (keyCode == KeyEvent.KEYCODE_DEL) { mText.delete(mText.length() - 1, mText.length()); Log.i("TAG", "text: " + mText + " (keycode)"); return true; } // TODO handle any other control keys here } else { // text character mText.append((char)event.getUnicodeChar()); Log.i("TAG", "text: " + mText + " (keycode)"); return true; } } return false; } }); } // toggle whether the keyboard is showing when the view is clicked @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, InputMethodManager.HIDE_IMPLICIT_ONLY); } return true; } @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { outAttrs.inputType = InputType.TYPE_CLASS_TEXT; // outAttrs.inputType = InputType.TYPE_CLASS_NUMBER; // alternate (show number pad rather than text) return new MyInputConnection(this, true); } } 

MyInputConnection.java

 public class MyInputConnection extends BaseInputConnection { private SpannableStringBuilder mEditable; MyInputConnection(View targetView, boolean fullEditor) { super(targetView, fullEditor); MyCustomView customView = (MyCustomView) targetView; mEditable = customView.mText; } @Override public Editable getEditable() { return mEditable; } // just adding this to show that text is being committed. @Override public boolean commitText(CharSequence text, int newCursorPosition) { boolean returnValue = super.commitText(text, newCursorPosition); Log.i("TAG", "text: " + mEditable); return returnValue; } } 

activity_main.xml

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.editorview.MainActivity"> <com.example.editorview.MyCustomView android:id="@+id/myCustomView" android:background="@android:color/holo_blue_bright" android:layout_margin="50dp" android:layout_width="300dp" android:layout_height="150dp" android:layout_centerHorizontal="true" /> </RelativeLayout> 

There is nothing special about MainActivity.java code.

Please leave a comment if this does not work for you. I am using this basic solution for a custom EditText in a library that I create, and if there are any edge cases in which it does not work, I want to know. If you want to view this project, a custom view is here . This is InputConnection here .

Similar

  • How to create a custom system keyboard
  • How to create a custom keyboard in an application
+13
Jun 03 '17 at 10:29 on
source share

I understand that your onKeyListener will receive hardware keyboard key events.

You will get access to all input events if you override boolean View.onKeyPreIme(int keyCode, KeyEvent event)

Thus, you can choose to handle the action of the key event [ DOWN | MULTIPLE | UP ] [ DOWN | MULTIPLE | UP ] [ DOWN | MULTIPLE | UP ] and return true or enable normal key processing ( return super.onKeyPreIme() )

+4
Apr 14 2018-11-11T00:
source share



All Articles