【问题标题】:DispatchKeyEvent to listen for Spacebar being pressedDispatchKeyEvent 监听空格键被按下
【发布时间】:2014-06-30 09:05:00
【问题描述】:

我正在开发一个 Android 项目,我需要检查何时按下空格键,以便我可以执行某个功能。

问题是,它在模拟器上运行,但在我的实际设备上却没有。我认为这可能是因为我的模拟器使用的是物理键盘,而不是屏幕上的虚拟键盘,但在实际设备上进行测试时,它使用的是虚拟键盘。

我正在尝试调度键事件

@Override
    public boolean dispatchKeyEvent(KeyEvent event)
    {

        if (event.getKeyCode() == KeyEvent.KEYCODE_SPACE
                && event.getAction() == KeyEvent.ACTION_UP)
        {
            QueryEditor queryEditor = (QueryEditor)getSupportFragmentManager().findFragmentById(R.id.fragment_queryEditor);
            queryEditor.formatQueryText();
            return true;
        }
        }

我也试过按下 on 键

@Override
    public boolean onKeyDown(int keyCode, KeyEvent event)
    {
        if (keyCode == KeyEvent.KEYCODE_BACK)
        {
            disconnectDatabase();
        }
        else if (keyCode == KeyEvent.KEYCODE_DEL)
        {
            QueryEditor queryEditor = (QueryEditor)getSupportFragmentManager().findFragmentById(R.id.fragment_queryEditor);
            queryEditor.formatQueryText();
        }

        return super.onKeyDown(keyCode, event);
    }

除非按下后退按钮,否则这些都不会被触发,但我需要空格键来触发事件。

感谢您提供的任何帮助。

更新

下面是我如何创建 QueryEditor 片段和创建事件处理程序的代码

@Override
    public void onActivityCreated(Bundle savedInstanceState)
    {
        super.onActivityCreated(savedInstanceState);

        iQueryEditor = (IQueryEditor)this;
        iErrorHandler = (IErrorHandler)this;

        txtQueryEditor = (EditText)getActivity().findViewById(R.id.query_txtQueryEditor);

        btnSubmitQuery = (ImageButton)getActivity().findViewById(R.id.query_btnPerformQuery);
        btnClearQuery = (ImageButton)getActivity().findViewById(R.id.query_btnDeleteQuery);

        txtQueryEditor.addTextChangedListener(new QueryTextChanged(getActivity(), txtQueryEditor, iQueryEditor));
        setDatabaseUsed();
        txtQueryEditor.setOnEditorActionListener(new OnEditorActionListener() {

            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER))
                        || actionId == EditorInfo.IME_ACTION_DONE)
                {
                    executeQuery();
                    return true;
                }
                else if (event.getKeyCode() == KeyEvent.KEYCODE_SPACE && event.getAction() == KeyEvent.ACTION_UP)
                {
                    Toast.makeText(getActivity(), "Space Bar Pressed", Toast.LENGTH_SHORT).show();
                    return true;
                }
                return false;
            }
        });

        btnSubmitQuery.setOnClickListener(mBtnSubmitQueryListener);
        btnClearQuery.setOnClickListener(mBtnDeleteQueryListener);
    }

txtQueryEditor 是我试图在其上接收空格键事件的 EditText。

【问题讨论】:

  • 此问题中的信息及其接受的答案可能会有所帮助:stackoverflow.com/questions/5419766/…
  • 你能写出如何激活软键盘按空格键吗?是 EditText 视图还是以编程方式完成?按空格键时焦点在哪里?
  • @Loop 它是由用户在弹出键盘的编辑文本内单击完成的,用户按下软空格键应该触发事件。
  • @Boardy 看看我的回答。我已经解释了关键事件是如何工作的,对于您的情况,代码示例的最后一部分将完成这项工作。只需按照我介绍的方式使用适当的侦听器即可。

标签: android


【解决方案1】:

部分答案在于KeyEvent API documentation

因为软输入法可以使用多种创新的输入方式 文本,不能保证软键盘上的任何按键都会 生成一个关键事件:这由 IME 自行决定,并且在 事实上,不鼓励发送此类事件。你永远不应该依赖 接收软输入法上任何键的 KeyEvents。特别是, 默认的软件键盘永远不会向任何人发送任何按键事件 面向 Jelly Bean 或更高版本的应用程序,并且只会发送事件 对于某些应用程序的删除和返回键的按下 以冰淇淋三明治或更早的时间为目标。注意其他软件 无论版本如何,输入法都可能永远不会发送按键事件。 如果需要,请考虑使用 IME_ACTION_DONE 等编辑器操作 与软件键盘的特定交互,因为它提供了更多 让用户了解您的应用程序将如何对密钥做出反应 按下。

这是一位 Google 工程师留下的comment

答案很简单。应用程序应该从不依赖密钥 管理用户输入的事件。这样做的原因是,它会导致 用户体验很差。

首先,它不起作用的情况太多了。它 不适用于 CJK 语言。这是行不通的 正确使用手势输入、语音输入或切换 输入,或任何新的创造性输入法开发人员可能会想出 将来。事实上,它唯一有效的情况是 输入体验最差的受限案例:遗留问题 类似硬件键盘的输入法,不适合 移动时代。

这就是为什么 Android 定义了一个丰富的 API,IME 通过它进行通信 到应用程序。它是方法不可知论者和语言不可知论者,并且 所有软件输入法都在实现它。应用程序是 应该使用这个API,使用EditText是最简单的 这样做的方法(更多内容见下文)。

请停止依赖传统的键事件来输入文本——完全不要。它 支持他们很好,但要求他们对每个人都不好。它 破坏了一致性,它将某些语言锁定在您的应用之外,并且 迫使用户的体验比他们预期的要差得多 安卓。正如评论 #14 中所指出的,输入法是明确的 完全没有义务发送关键事件,尽管有些人可能会选择 去做吧。

有两种简单的方法可以实现富文本输入 API。 两者都需要在 Java 领域完成其中的一些。第一个是 简单地使用 EditText。如果你的应用需要自己渲染,你 如果适合您,可以将其子类化,或者在您使用时将其隐藏 呈现对文本的任何更改。您还可以使用 DynamicLayout 和 可编辑以达到相同的效果。另一种可能是 通过扩展直接实现 InputConnection API BaseInputConnection,但这是一项大量的艰苦工作,应该 通常只需要由具有非常具体的应用程序来完成 文本编辑方面的需求,例如 IDE 或文字处理器。

【讨论】:

    【解决方案2】:

    你的 Activity 中的这个实现应该可以工作:

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_SPACE && event.getAction() == KeyEvent.ACTION_UP) {
            Log.d("test", "[Dispatch] Space bar pressed! " + event);
            return true;
        }
        return super.dispatchKeyEvent(event);
    }
    

    与您的代码不同的是,我为除 SPACE_BAR 之外的所有其他键调用 super.dispatchKeyEvent()。如果 dispatchKeyEvent 返回 true onKeyUp() 将不会被触发。因此,如果您只想监视空格键事件,只需注释行//return true;

    如果您使用onKeyUp() 方法,它也可以正常工作。请勿使用onKeyDown(),如果用户手指握得太久,可能会调用几次。

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_SPACE) {
            Log.d("test", "Space bar pressed!");
            return true;
        }
        return super.onKeyUp(keyCode, event);
    }
    

    这里我使用类似的方法。如果您的 if 语句为 true,则处理事件并返回 true。对于其余的密钥,请致电 super.onKeyUp();

    至少,但最重要的是,如果您有拥有当前焦点的视图(例如 EditText)并且为该视图显示键盘,则根本不会调用上述代码(在 Activity 中)。如果是这种情况,您必须实现侦听器TextView.OnEditorActionListener 并注册该视图调用setOnEditorActionListener(TextView.OnEditorActionListener l)

    viewWithFocus.setOnEditorActionListener(new TextView.OnEditorActionListener() {
        @Override
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            if (event.getKeyCode() == KeyEvent.KEYCODE_SPACE && event.getAction() == KeyEvent.ACTION_UP) {
                Log.d("test", "[Editor] Space bar pressed! " + event);
                //TODO implement your action here
                return true;//remove this line if you want edit text to create space
            }
            return false;
        }
    });
    

    作为替代方案,您可以覆盖此视图并像上面一样实现onKepUp()

    更新

    上述解决方案适用于硬件键盘。很抱歉没有仔细检查。

    来自 Android 文档

    注意:使用 KeyEvent 类处理键盘事件时, 相关的 API,您应该期望此类键盘事件仅出现 从硬件键盘。你永远不应该依赖接收密钥 软输入法(屏幕键盘)上任何键的事件。

    但是,我找到了解决此问题的方法。我基于 SearchView 组件进行了研究,发现以下代码可以完成这项工作:

    editText.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }
    
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            char last = s.charAt(s.length()-1);
            if (' ' == last) {
                Log.i("test", "space pressed");
            }
        }
    
        @Override
        public void afterTextChanged(Editable s) {
        }
    });
    

    对于 IME 操作,请继续使用 TextView.OnEditorActionListener

    【讨论】:

    • 感谢您的帮助我已经尝试过了。不过,它似乎对我不起作用。我想我忘了提,不知道是不是原因,但 EditText 在活动托管的片段内。我尝试通过片段和活动设置事件侦听器,但都没有触发事件
    • 你能粘贴更多代码吗?特别是在设置 EditText 和使用此 EditText 的 XML 文件的地方。 TextView.OnEditorActionListener 绝对是正确的选择,你必须做其他事情来阻止它。
    • @IllegalArgument 看到您的原始答案后,我没有再看它。不要无端指责我。
    【解决方案3】:

    我尝试了 dispatchkeyevents 和 onkeydown 这两种方法,但对于软键盘,它们都不起作用。至少不在nexus 7中。唯一有效的解决方案是在edittext中添加一个textwatcher。下面是代码:

    public class MainActivity extends ActionBarActivity {
    EditText editText1;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editText1 = (EditText) findViewById(R.id.editText1);
        editText1.addTextChangedListener(new TextWatcher() {
    
            @Override
            public void afterTextChanged(Editable arg0) {
                // TODO Auto-generated method stub
    
            }
    
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count,
                    int after) {
                // TODO Auto-generated method stub
    
            }
    
            @Override
            public void onTextChanged(CharSequence s, int start, int before,
                    int count) {
                // TODO Auto-generated method stub
                String lastChar = s.toString().substring(s.length() - 1);
                if (lastChar.equals(" ")) {
                    Toast.makeText(MainActivity.this, "space bar pressed",
                            Toast.LENGTH_SHORT).show();
                }
            }
    
        });
    }
    
    }
    

    【讨论】:

    • 你的回答大错特错。关键代码和动作是完全不同的东西,没有单独提供。此外,将 keyCode 值与 ACTION_UP 进行比较是没有意义的。
    猜你喜欢
    • 2020-11-16
    • 2012-12-27
    • 1970-01-01
    • 2012-08-05
    • 1970-01-01
    • 2019-09-07
    • 2011-06-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多