如何在视图中捕获软键盘输入?

ric*_*h.e 48 android

我有一个子类视图,当它在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...
        // .
    }
}
Run Code Online (Sandbox Code Playgroud)

键盘显示,但是当我按下键盘上的"下一步"按钮时,我的onKeyListener才会触发.我需要点击哪个字符,以便我可以在我的onDraw()方法中显示它.

Car*_*arl 45

实际上,您可以自己处理关键事件,而无需从TextView派生您的视图.

为此,只需修改原始代码,如下所示:

1)替换以下行 onCreateInputConnection():

outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
Run Code Online (Sandbox Code Playgroud)

这一个:

outAttrs.inputType = InputType.TYPE_NULL;
Run Code Online (Sandbox Code Playgroud)

根据文档InputType.TYPE_NULL: "这应该被解释为意味着目标输入连接不丰富,它不能处理和显示候选文本之类的东西,也不能检索当前文本,因此输入方法需要在有限的'生成密钥中运行事件模式."

2)以相同的方法替换以下行:

BaseInputConnection fic = new BaseInputConnection(this, true);
Run Code Online (Sandbox Code Playgroud)

这一个:

BaseInputConnection fic = new BaseInputConnection(this, false);
Run Code Online (Sandbox Code Playgroud)

false second参数将BaseInputConnection置于"虚拟"模式,这也是将原始键事件发送到视图所必需的.在BaseInputConnection代码中,您可以找到以下几条注释:"仅当虚拟模式,为新文本发送键事件并清除当前可编辑缓冲区时".

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

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

但遗憾的是,似乎没有一种简单的方法来配置EditorInfo属性而不覆盖这些方法并提供BaseInputConnection对象,并且一旦完成,您将不得不愚蠢地执行该对象所执行的处理,如上所述想再次收到原始关键事件.

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

Android - 无法捕获退格/删除按下软.键盘

  • 对于那些感兴趣的人,我现在正在处理的问题之一是自定义键盘不支持 `InputType.TYPE_NULL`。具体来说,Minuum 键盘似乎完全忘记了设置,这破坏了我的应用程序。 (2认同)

ric*_*h.e 22

事实证明我确实需要继承TextView并使用addTextChangedListener()来添加我自己的TextWatcher实现以便监听软键事件.我用一个普通的旧视图找不到办法.

另一件事,对于那些尝试这种技术的人; TextView默认情况下无法编辑文本,所以如果你想让你的实现可编辑(而不是继承EditText,我不想这样做),你还必须创建一个自定义的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后使用以下内容覆盖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;
 }
Run Code Online (Sandbox Code Playgroud)

在此之后,您应该有一个可以收听软键盘的View,您可以使用键输入值执行任何操作.


Sur*_*gch 19

根据文档,View(编辑器)通过一个键盘(IME)接收命令,InputConnection并通过命令向键盘发送命令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;
}
Run Code Online (Sandbox Code Playgroud)

视图也需要setFocusableInTouchMode(true)先前设置.

2.从键盘接收输入

为了使视图从键盘接收输入,它需要覆盖onCreateInputConnection().这将返回InputConnection键盘用于与视图通信的内容.

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我们在这里所做的就是在自定义视图中提供输入连接以及对文本变量的引用.在BaseInputConnection将编辑该照顾mText.这很可能就是你需要做的一切.但是,您可以查看源代码并查看哪些方法说"默认实现",尤其是"默认实现什么都不做".这些是您可能想要覆盖的其他方法,具体取决于编辑器视图的相关程度.您还应该查看文档中的所有方法名称.他们中的许多人都有"编辑作者"的笔记.特别注意那些.

某些键盘InputConnection由于某种原因(例如删除,输入和某些数字键盘键)不会发送某些输入.对于那些我添加了一个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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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

如果这不适合您,请发表评论.我正在使用这个基本解决方案在我正在制作的库中的自定义EditText,如果有任何边缘情况,它不起作用,我想知道.如果要查看该项目,则自定义视图位于此处.这InputConnection在这里.

有关