如何捕捉视图中的软键盘input?

我有一个subclassed视图popup键盘,当它在onTouchEvent接收到“触摸”。 它通过请求焦点,检索InputMethodManager,然后调用showSoftInput来显示。

现在我需要弄清楚如何捕捉软键盘的敲击字母, 因为它们被按下 。 我目前只有在软键盘上按下“下一步/完成”button才能得到响应。

这是我的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... // . } } 

键盘显示,但我的onKeyListener只有当我按下键盘上的“下一步”button时触发。 我需要点击哪个angular色,以便我可以在我的onDraw()方法中显示它。

实际上可以自己处理关键事件,而无需从TextView中获取视图。

要做到这一点,只需修改您的原始代码如下:

1)replaceonCreateInputConnection():的以下行onCreateInputConnection():

 outAttrs.inputType = InputType.TYPE_CLASS_TEXT; 

与这一个:

 outAttrs.inputType = InputType.TYPE_NULL; 

根据InputType.TYPE_NULL:的文档InputType.TYPE_NULL: “这应该被解释为意味着目标input连接不丰富,它不能处理和显示候选文本的东西,也不能检索当前的文本,所以input法需要运行在有限的“生成关键事件”模式。

2)用相同的方法replace下面的行:

 BaseInputConnection fic = new BaseInputConnection(this, true); 

与这一个:

 BaseInputConnection fic = new BaseInputConnection(this, false); 

错误的第二个参数将BaseInputConnection置于“虚拟”模式,这也是为了将原始键事件发送到您的视图所必需的。 在BaseInputConnection代码中,可以find几个注释,如下所示:“只有在虚拟模式下,才会为新文本发送关键事件,并清除当前可编辑缓冲区。

我已经使用这种方法让软键盘将原始事件发送到从LinearLayout派生的我的视图(即,不是从TextView派生的视图),并可以确认它是否有效。

当然,如果您不需要设置IME_ACTION_DONE imeOptions值来显示键盘上的Donebutton,那么您可以完全删除onCreateInputConnection()onCheckIsTextEditor()覆盖,然后将原始事件发送到您的视图默认情况下,因为没有能够进行更复杂处理的input连接将被定义。

但不幸的是,似乎没有一种简单的方法来configurationEditorInfo属性,而不必重写这些方法并提供一个BaseInputConnection对象,一旦你完成了这个工作,你将不得不笨拙地按照上面描述的那样对该对象执行的处理,如果你想要再次收到原始的关键事件。

警告:在Android(Google键盘)附带的某些最新版本的默认LatinIME键盘中引入了两个错误,这些键盘在使用时可能会影响键盘事件处理(如上所述)。 我已经devise了一些应用程序方面的解决方法,例如代码,似乎解决了这些问题。 要查看这些解决方法,请参阅以下答案:

Android – 无法捕捉退格/删除软按。 键盘

事实certificate,我确实需要inheritanceTextView,并使用addTextChangedListener()来添加我自己的TextWatcher实现,以便收听软按键事件。 我找不到一个方法来做一个普通的旧视图。

另一件事,对于那些将尝试这种技术的人来说, TextView不能编辑默认的文本,所以如果你想让你的实现可编辑(而不是inheritanceEditText,我不想这样做),你还必须做一个自定义的InputConnection,如下所示:

  /** * 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; } } 

然后你重写onCheckisTextEditor和onCreateInputConnection,如下所示:

  @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; } 

在此之后,您应该有一个视图,可以听软键盘,你可以做任何你想要的键input值。

根据文档 , View (编辑器)通过InputConnection从键盘(IME)接收命令,并通过InputMethodManager向键盘发送命令。

在这里输入图像描述

我将在下面显示整个代码,但这里是步骤。

1.使键盘出现

由于视图正在向键盘发送命令,因此需要使用InputMethodManager 。 为了这个例子,我们会说,当视图被点击时,它会显示键盘(或者如果它已经显示,就隐藏)。

 @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; } 

该视图还需要先前已经设置了setFocusableInTouchMode(true)

2.接收来自键盘的input

为了让视图接收来自键盘的input,它需要重写onCreateInputConnection() 。 这将返回键盘用于与视图进行通信的InputConnection

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

outAttrs指定视图请求的键盘types。 这里我们只是要求一个普通的文本键盘。 selectTYPE_CLASS_NUMBER将显示一个数字键盘(如果可用)。 还有很多其他的select。 请参阅EditorInfo

您必须返回一个InputConnection ,它通常是BaseInputConnection一个自定义子类。 在该子类中,您提供了一个对可编辑string的引用,键盘将对其进行更新。 由于SpannableStringBuilder实现了Editable接口,我们将在我们的基本示例中使用它。

 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; } } 

我们在这里所做的只是在自定义视图中提供了对文本variables的引用的input连接。 BaseInputConnection将负责编辑该mText 。 这可能就是你需要做的一切。 但是,您可以查看源代码并查看哪些方法表示“默认实现”,特别是“默认实现什么都不做”。 这些是你可能想要重写的其他方法,具体取决于你的编辑器视图将会如何。 您还应该查看文档中的所有方法名称。 他们中的一些人有“编辑作者”的笔记。 要特别注意这些。

由于某些原因,某些键盘不会通过InputConnection发送某些input(例如, 删除 ,input和某些数字键盘键 )。 对于那些我添加了一个OnKeyListener 。 在五个不同的软键盘上testing这个设置,一切似乎工作。 与此相关的补充答案在这里:

  • 在Android KeyEvent中区分控制键码中的文本键码
  • 需要android和主持人的关键代码表
  • 数字型键盘的input连接

完整的项目代码

这里是我的完整例子供参考。

在这里输入图像描述

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> 

MainActivity.java代码没有什么特别之处。

如果这不适合你,请留下评论。 我正在使用这个基本的解决scheme,我正在一个库中的自定义EditText,如果有任何边缘情况下,它不工作,我想知道。 如果你想查看该项目,自定义视图在这里 。 这是InputConnection在这里 。

有关

  • 如何制作自定义系统键盘
  • 如何制作自定义的应用内键盘

我的理解是你的onKeyListener只会获得硬件键盘按键事件。

如果您重写boolean View.onKeyPreIme(int keyCode, KeyEvent event)您将可以访问所有input键事件

这样你可以select处理关键事件动作[ DOWN | MULTIPLE | UP ] [ DOWN | MULTIPLE | UP ] [ DOWN | MULTIPLE | UP ]并返回true ,或者允许正常的键处理来处理它( return super.onKeyPreIme()