美文网首页
android 键盘输入拦截与转发 2019-08-21

android 键盘输入拦截与转发 2019-08-21

作者: 讨厌西红柿 | 来源:发表于2019-08-21 22:43 被阅读0次

    之前记录的一篇android使用扫码枪被系统键盘拦截的问题 2019-08-13存在误导,这里重新记录下问题解决方案。

    场景

    android端外接扫码枪、键盘等输入设备,在完全没有input组件的时候,如果按键输入一些字符,会发生什么情况呢?原生安卓,可能并没有大问题,但是如果你是使用了webview呢?

    可以简单测试下,就一个空的activity,嵌入一个WebView,什么都不加载,使用外接键盘输入字符,你会发现软键盘莫名其妙的弹出来了???(版本是android5.1,6.0以上的好像就不会)。

    并且弹出键盘会导致WebViewonkeypress方法接收不到输入事件,无法进行之后的一系列操作。

    至于什么问题,很遗憾,我查阅了一些资料也没找到答案。

    解决思路

    由于是WebView导致的键盘弹出,那索性就让其没有焦点,这总不会弹出键盘了吧。

    webView.setFocusable(false);
    

    加上以上代码,确实键盘也不弹出了,但是WebView也收不到输入事件了。既然如此,那就由android端拦截键盘输入再转发给web不就好了?

    那就重写activitydispatchKeyEvent方法,所有的输入事件都会经过它分发出去。其boolean类型的返回值,返回true表示事件已经被我处理了,你不要再传给其他人了;返回false则表示,我不处理,你传给其他人处理吧。

    @Override
        public boolean dispatchKeyEvent(KeyEvent event) {
            int keyCode = event.getKeyCode();
    
            checkLetterStatus(event);
    
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                char ch = getInputCode(event);
                if(ch == 0){
                    return super.dispatchKeyEvent(event);
                }else{
                    String codeStr = String.valueOf(ch);
                    this.callWebHandler(JsMessage.ON_KEYCODE_EVENT, new WebKeyEvent(keyCode,codeStr));
                    return true;
                }
            }
    
            return super.dispatchKeyEvent(event);
        }
    

    使用checkLetterStatus方法记录shift状态,再判断是否为KeyEvent.ACTION_DOWN按下按键时,因为一次按键包含两个操作:keydown和keyup,也会触发两次dispatchKeyEvent,所以需要做一次判断隔离。然后getInputCode方法筛选需要的字符,不需要的字符则返回0,抛给父类处理,需要的字符则通过callWebHandler直接传给了Web。

    checkLetterStatus的实现很简单,单纯的记录shift的按下状态

    private Boolean mCaps = false;
    
        private void checkLetterStatus(KeyEvent event) {
            int keyCode = event.getKeyCode();
            if (keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT || keyCode == KeyEvent.KEYCODE_SHIFT_LEFT) {
                if (event.getAction() == KeyEvent.ACTION_DOWN) {
                    //按着shift键,表示大写
                    mCaps = true;
                } else {
                    //松开shift键,表示小写
                    mCaps = false;
                }
            }
        }
    

    getInputCode方法则主要负责将keyCode转成字符

    private char getInputCode(KeyEvent event) {
    
            int keyCode = event.getKeyCode();
            char aChar = 0;
    
            if (keyCode >= KeyEvent.KEYCODE_A && keyCode <= KeyEvent.KEYCODE_Z) {
                //字母
                aChar = (char) ((mCaps ? 'A' : 'a') + keyCode - KeyEvent.KEYCODE_A);
            } else if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
                //数字
                aChar = caseNumber(keyCode);
            } else {
                //其他符号
                switch (keyCode) {
                    case KeyEvent.KEYCODE_PERIOD:
                        aChar = mCaps?'>':'.';
                        break;
                    case KeyEvent.KEYCODE_MINUS:
                        aChar = mCaps ? '_' : '-';
                        break;
                    case KeyEvent.KEYCODE_SLASH:
                        aChar = mCaps?'?':'/';
                        break;
                    case KeyEvent.KEYCODE_BACKSLASH:
                        aChar = mCaps ? '|' : '\\';
                        break;
                    case KeyEvent.KEYCODE_NUMPAD_MULTIPLY:
                        aChar = '*';
                        break;
                    case KeyEvent.KEYCODE_ENTER:
                        aChar = '\n';
                        break;
                    case KeyEvent.KEYCODE_EQUALS:
                        aChar = mCaps?'+':'=';
                        break;
                    default:
                        break;
                }
            }
    
            return aChar;
        }
    

    如果输入是字母,就用keyCode减去A键的值KeyEvent.KEYCODE_A,再根据之前记录的mCaps值加回字符'A'或者'a'控制其大小写。如果是数字,则使用caseNumber方法返回对应的字符。其他字符,则使用了switch一个个列举,根据mCaps的值看着键盘一个个敲。

    caseNumber方法如下实现

    private char caseNumber(int keyCode){
            char ch = (char) ('0' + keyCode - KeyEvent.KEYCODE_0);
            if(!mCaps){
                return ch;
            }
            switch (keyCode){
                case KeyEvent.KEYCODE_0:return ')';
                case KeyEvent.KEYCODE_1:return '!';
                case KeyEvent.KEYCODE_2:return '@';
                case KeyEvent.KEYCODE_3:return '#';
                case KeyEvent.KEYCODE_4:return '$';
                case KeyEvent.KEYCODE_5:return '%';
                case KeyEvent.KEYCODE_6:return '^';
                case KeyEvent.KEYCODE_7:return '&';
                case KeyEvent.KEYCODE_8:return '*';
                case KeyEvent.KEYCODE_9:return '(';
            }
    
            return ch;
        }
    

    没有shift按键操作,则直接返回ch;否则,就列举0-9所有的上行符号。

    安卓端已经处理完毕,接下来就是web端的逻辑

    onAndroidKeyEvent(keyObj){
        if(!this.allowInput || this.state.visible){
          // 不允许输入及结算流程,不处理输入
          return;
        }
        // console.log("keyCode = ", keyCode);
        let num = keyObj.keyCode;
        console.log("num = ", num);
        if (num !== 66) {
          if(!this.codeStr || this.codeStr.length === 0){
            // 输入超时
            setTimeout(()=>{
              this.codeStr = "";
            }, 1000);
          }
          // this.codeStr += String.fromCodePoint(num);
          this.codeStr += keyObj.codeStr;
        } else {
          this.allowInput = false;
          setTimeout(()=>{
            this.allowInput = true;
          },1000);
    
          let value  = this.codeStr;
          this.codeStr = "";
          // ... do some next
        }
    
      }
    

    其实也没啥逻辑,就是加了个超时,1秒内没有输入回车则丢弃。66是android端回车的keyCode。

    至此,web端已经能接收到外接设备的输入值,并且也不再会弹出键盘。

    相关文章

      网友评论

          本文标题:android 键盘输入拦截与转发 2019-08-21

          本文链接:https://www.haomeiwen.com/subject/izovsctx.html