一、背景

最近一个需求是,在PDA上实现获取扫码枪扫描结果, 由于广播action未知,故使用扫码器的模拟输入功能获取其扫码结果,并不显示到前端,而是后台继续处理。

起初,是很简单的一个问题,直接使用FocusScope组件,监听键盘事件即可, 仅仅如此则安好。

then, 由于界面上还存在其他输入框类的组件,偶然间发现,当焦点从输入框出来之后,便不可从focusScope获取到模拟键盘的输入信息了, 但是物理按键还可以继续监测。偶然发现一个警告信息
在这里插入图片描述
意思说是FocusScope 不存在inputMehodQuery方法,(在此留下一个疑问,有了解的同僚评论区指导一下: 在不进入输入框之前为何可以监测,从输入框出来后却不可以了,难道被输入框吃掉了吗,可以确定焦点在FocusScope上), 搜索无果,因为是由inputMethodQuery造成的,很明显地把我的灵感引导到了输入框上。

于是,就有了从输入框获取扫码结果的方案,将输入框隐藏,但焦点还是存在的,方法百试百灵,唯一不足,当输入框获取焦点时,会自动弹出安卓上的输入法。

接下来肯定就是不让软键盘弹出来,于是就有了本文隐藏输入法软键盘的问题。

二、过程

经过大量的网上冲浪, 很多是将使用qt的方法进行关闭,但是并不适合,相信总有qml的方法。

于是,苍天不负有心人,从这个代码中找到了关键点 Qt.inputMethod.hide(), 大致结构如下

TextInput{
    id: inputForScancode
    z: 10
    focus: true
    visible: false
    onActiveFocusChanged: {
        if(activeFocus){
            Qt.inputMethod.hide()
        }
    }
}

直接使用时发现,然并卵。 是我操作失误??? 看起来挺对的啊,一定是哪里出错了!!!
于是改用按钮测试,在onClicked里测试该代码Qt.inputMethod.hide(), 果然有效果。
那差不多就有了眉目。

三、问题解决

该现象就像在王者里面的预判过早现象,对手技能还没放出来,你闪现已经交了,并没有起到躲避技能的作用,白白浪费了闪现,做了无用功。

于是, 以下代码诞生了, 当我输入框获取焦点后,我不立即去隐藏输入法,而是等待几ms之后再去关闭,这样就没有问题了,只要我定时时间控制足够精确,就能把输入法扼杀于摇篮之中。

Timer{
    id: closeInputMethod
    interval: 50
    onTriggered: {
        Qt.inputMethod.hide()
    }
}
TextInput{
    id: inputForScancode
    focus: true
    visible: false
    onActiveFocusChanged: {
        if(activeFocus){
            // 启动定时器,定时实现关闭输入法
            closeInputMethod.start()
        }
    }
    Keys.onReleased: {
        if(event.key === Qt.Key_Return){ // 扫码结束
            barcodeText = inputForScancode.text
        }

        if(event.key === Qt.Key_F1 || event.key ===Qt.Key_F2){ // 松开PDA扫码键
            inputForScancode.text = ""
            barcodeText = ""
        }
    }
}

四、进阶版解决方案

该方案的诞生:
有时一次并不能将软键盘关掉,故给他100ms的时间调用关闭多次,确保可以关掉,唯一缺点是小概率会产生副作用——看到软键盘一闪而过

Timer{
    id: closeInputMethod
    interval: 20
    running: inputForScancode.activeFocus || inputForScancode.focus
    triggeredOnStart: true
    repeat: true
    onTriggered: {
        Qt.inputMethod.hide()
    }
}

Timer{
    id:closeCloseInputMehodTimer
    interval: 100
    running: inputForScancode.activeFocus || inputForScancode.focus
    onTriggered: {
        closeInputMethod.stop()
    }
}

TextInput{
    id: inputForScancode
    focus: true
}

五、切换焦点的方案

以下代码实现了监测全局焦点的改变,当判断不是TextInput等输入框组件时,则切换到inputForScancode组件去。

技术要点:

  • 监测focusObjec变量,并通过变量 activeFocusItem 来指示当前获取焦点的组件。
  • activeFocusItem 可能为null, 需要单独处理
onFocusObjectChanged: {
    console.log("the focuss is changed main", activeFocusItem)
    if(activeFocusItem == inputForScancode){
        return
    }

    if(activeFocusItem != null){
        if(!(/TextField/.test(activeFocusItem.toString()) || /.test(activeFocusItem.toString()))){
	        focusToScan()
        }
    }else{
        focusToScan()
    }
}

六、 切回焦点的注意事项

注: 在切换回焦点时,切记不要直接执行forceActiveFocus(), 这样可能导致软件盘无法关闭, 使用以下代码恢复焦点

    Timer{
        id: forceToScanFocus
        interval: 100
        onTriggered: {
            inputForScancode.forceActiveFocus()
        }
    }
    function focusToScan(){
        inputForScancode.forceActiveFocus()
    }

上述代码的延时目的是: 确保textinput已失去焦点并重新获得,如此才会触发定时器去关闭软键盘。

五、结语

如果对你有帮助,别忘了一键三连,点赞 、关注、收藏、评论

更多推荐