我有一个子类视图,当它在onTouchEvent中收到“修饰”时弹出键盘。它通过请求焦点,检索InputMethodManager,然后调用showSoftInput。
来显示现在我需要弄清楚如何捕捉软键盘的轻敲字母,因为它们被按下。我目前只在软键盘上按下“下一个/完成”按钮时得到响应。
这是我的班级:
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才会触发。我需要点击哪个字符,以便我可以在我的onDraw()方法中显示它。
答案 0 :(得分:40)
实际上,您可以自己处理关键事件,而无需从TextView派生您的视图。
为此,只需修改原始代码,如下所示:
1)替换onCreateInputConnection():
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
这一个:
outAttrs.inputType = InputType.TYPE_NULL;
根据InputType.TYPE_NULL:
的文档“这应解释为目标输入连接不丰富,它不能处理和显示候选文本之类的内容,也不能检索当前文本,因此输入方法将需要在有限的“生成关键事件”模式下运行。“
2)用同样的方法替换以下行:
BaseInputConnection fic = new BaseInputConnection(this, true);
这一个:
BaseInputConnection fic = new BaseInputConnection(this, false);
false second参数将BaseInputConnection置于“虚拟”模式,也模式,以便将原始密钥事件发送到您的视图。在BaseInputConnection代码中,您可以找到多个注释,例如:“仅当虚拟模式,为新文本发送键事件并清除当前可编辑缓冲区时。”
我已经使用这种方法让软键盘将原始事件发送到我的视图,该视图源自LinearLayout(即,不是从TextView派生的视图),并且可以确认它是否有效。
当然,如果您不需要设置IME_ACTION_DONE
imeOptions值来在键盘上显示“完成”按钮,那么您可以完全删除onCreateInputConnection()
和onCheckIsTextEditor()
覆盖,默认情况下,原始事件将发送到您的视图,因为不会定义能够进行更复杂处理的输入连接。
但遗憾的是,似乎没有一种简单的方法来配置EditorInfo属性而不覆盖这些方法并提供BaseInputConnection对象,并且一旦完成,您将不得不愚蠢地执行该对象所执行的处理如果您想再次收到原始密钥事件,请在上面。
警告:Android(Google键盘)附带的默认LatinIME键盘的某些最新版本中引入了两个错误,这些错误会在使用该键盘时影响键盘事件处理(如上所述)。我已经在应用程序方面设计了一些变通方法,带有示例代码,似乎可以解决这些问题。要查看这些变通方法,请参阅以下答案:
Android - cannot capture backspace/delete press in soft. keyboard
答案 1 :(得分:21)
事实证明我确实需要继承TextView并使用addTextChangedListener()来添加我自己的TextWatcher实现以便侦听软键事件。我用一个普通的旧视图找不到办法。
另一件事,对于那些尝试这种技术的人; TextView默认情况下无法编辑文本,所以如果你想让你的实现可编辑(而不是继承EditText,我不想这样做),你还必须创建一个自定义的InputConnection,如下所示: / p>
/**
* 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;
}
在此之后,您应该有一个可以收听软键盘的视图,您可以使用键输入值执行任何操作。
答案 2 :(得分:16)
根据documentation,View
(编辑)通过InputConnection
从键盘(IME)接收命令,并通过InputMethodManager
向键盘发送命令。
我将在下面显示整个代码,但这里是步骤。
由于视图正在向键盘发送命令,因此需要使用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)
。
要使视图从键盘接收输入,需要覆盖onCreateInputConnection()
。这将返回键盘用于与视图通信的InputConnection
。
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
return new MyInputConnection(this, true);
}
outAttrs
指定视图请求的键盘类型。这里我们只是要求一个普通的文本键盘。选择TYPE_CLASS_NUMBER
将显示数字键盘(如果可用)。还有很多其他选择。请参阅EditorInfo
。
您必须返回InputConnection
,这通常是BaseInputConnection
的自定义子类。在该子类中,您提供对可编辑字符串的引用,键盘将对其进行更新。由于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;
}
}
我们在这里所做的只是在自定义视图中提供输入连接以及对文本变量的引用。 BaseInputConnection
将负责编辑mText
。这很可能就是你需要做的一切。但是,您可以查看source code并查看哪些方法说"默认实施",尤其是"默认实施什么都不做。"这些是您可能想要覆盖的其他方法,具体取决于编辑器视图的相关程度。您还应该查看documentation中的所有方法名称。他们中的许多人都对编辑作者和#34;做了笔记。特别注意那些。
由于某些原因,某些键盘无法通过InputConnection
发送某些输入(例如delete,输入,以及某些number pad keys)。对于那些我添加OnKeyListener
的人。在五个不同的软键盘上测试这个设置,一切似乎都有效。与此相关的补充答案如下:
以下是我的完整示例供参考。
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代码没有什么特别之处。
如果这对您不起作用,请发表评论。我正在使用这个基本解决方案在我正在制作的库中自定义EditText,如果有任何边缘情况,它不起作用,我想知道。如果您要查看该项目,则自定义视图为here。它的InputConnection
是here。
答案 3 :(得分:4)
我的理解是你的onKeyListener
只会获得硬件键盘键事件。
如果您覆盖boolean View.onKeyPreIme(int keyCode, KeyEvent event)
这样您就可以选择处理关键事件操作[ DOWN | MULTIPLE | UP ]
并返回true
,或允许正常的密钥处理来处理它(return super.onKeyPreIme()
)