最近做的这个电视机APP,适配了触控屏,如果要兼容性更好,还得支持遥控操作才行。因为产品架构的原因,要支持遥控器控制的话,需要做兼容处理的就有两处地方了。1、APP的配置地址页面。2、H5应用的页面。
APP的配置页面,使用的nvue编写的,不支持window对象,所以没有办法使用window的事件监听来获取按键编码。但是uni-app本身支持H5+,所以可以通过H5+使用plus.key.addEventListener监听keydown事件就可以了。在nvue页面遥控器控制最麻烦的事情在于聚焦当前选择的内容。好在本应用中,需要进行控制的,只有几个输入框和一个建立连接的按钮。统一设置一个选中下标,根据方向键监听修改下标值,再根据下标值触发对应的输入框的聚焦事件。
实现代码
onShow() { const path = getWebPageUrlIpAndPort() if (path) { this.ip = path.split(':')[0].split('.') this.host = path.split(':')[1] } const keyDownFun = (e) => { // 上 if (e.keyCode === 19) { // 下 } else if (e.keyCode === 20) { // 左 } else if (e.keyCode === 21) { if (this.currIndex === 0) { // 重置为4 this.currIndex = 4 this.$refs[`input4`].focus() } else if (0 < this.currIndex && this.currIndex <= 4) { this.currIndex-- this.$refs[`input` + this.currIndex].focus() } // 右 } else if (e.keyCode === 22 || e.keyCode === 61) { if (this.currIndex === 4) { this.currIndex = 0 this.$refs[`input0`].focus() } else if (0 <= this.currIndex && this.currIndex < 4) { this.currIndex++ this.$refs[`input` + this.currIndex].focus() } // 回车 } else if (e.keyCode === 66) { // 触发 this.registerDevice() } } this.keyDownFun = keyDownFun plus.key.addEventListener("keydown", keyDownFun, false); plus.key.addEventListener("menubutton", this.registerDevice) }
H5页面的遥控器兼容就出了一点问题,最开始的研究方向就不太对。由于没有经验,需要确认在H5页面中,使用遥控器触发的是否同样是keydown事件,所以特意给windows对象监听了keydown事件。使用遥控器按方向键、确认键,如愿触发了方法,可是遥控器的菜单键和返回键确毫无响应。本来都打算在uni-app页面中监听H5plus的事件,再将消息发送的H5应用中。结果这种方案行不通,在承载h5应用的webview所在nvue页面,使用backbutton事件监听无法生效。同时在方向键的逻辑处理中也陷入了泥沼,原本是想在方向键触发后,给选中的元素添加一个选中效果,结果在添加选中效果时,添加选中效果的元素与当前实际选中的元素不一致。在经过一段时间的研究后,最终解决了这两个问题,一个是查找资料时网友给我的灵感,另一个是走出思维误区后迎刃而解。
首先来看看第一个问题,返回键和菜单键失效的问题,在DClOUD问答中,网友也是类似的场景,在webview页面无法监听到backbutton事件。解决方案是,新开页面均需要自己监听事件。所以,我猜测就是这种情况。在H5应用中引入了mui,再使用window.plus.key.addEventListener监听backbutton事件,完美解决问题!
代码:
// 监听H5Plus准备事件 window.document.addEventListener('plusready', () => { let first = null // 监听返回键 window.plus.key.addEventListener('backbutton', () => { const manpowerTable = document.querySelector('.wapper-wrap-mask') if (this.$route.name === 'manpowerstructuremanpowerstructure' && manpowerTable.style.display !== 'none') { manpowerTable.style.display = 'none' } else { // 首次按键,提示‘再按一次退出应用’ if (!first) { first = new Date().getTime() // 获取第一次点击的时间戳 window.plus.nativeUI.toast('再按一次退出应用', { duration: 'short' }) // 通过H5+ API 调用Android 上的toast 提示框 setTimeout(function() { first = null }, 1000) } else { if (new Date().getTime() - first < 1000) { // 获取第二次点击的时间戳, 两次之差 小于 1000ms 说明1s点击了两次, window.plus.runtime.quit() // 退出应用 } } } }) // 监听菜单键 window.plus.key.addEventListener('menubutton', () => { // 当前路由不在 无数据页面 if (this.$route.path !== '/noContent/noContent') { // 可以触发菜单显示操作 this.$EventBus.$emit('remoteActiveMenu', '') } }, false) }, false)
第二个问题,陷入了思维误区,方向键移动,在移动端中,本身就进行元素选中,需要做的仅仅只是更改选中元素的样式,并不需要监听方向键移动事件,再对选中元素进行更改样式,否则就会出现有样式的元素与实际选中元素不相符合的情况。获取选中元素,可以使用window.document.activeElement,在尝试过程我是通过JSON.stringify(window.document.activeElement.outerHTML)方式确定当前选中的元素的。不过这种方式有一定的缺陷,方向键可选元素,我暂时无法控制,没有研究出来方案,也许是可以支持的。所以就会出现在方向键切换时,当前选中的元素并不是我们需要进行选中的元素,这里我通过另一种方案进行处理。在需要支持选中的元素上增加特定的class,在监听聚焦事件方法中,根据选择元素是否存在特定class来决定是否添加选中样式,同时,在监听确定事件中,也根据特定的class来决定是否触发确定逻辑。这样能在一定程度上解决问题。
代码:
// 监听keydown事件 window.document.onkeydown = (e) => { const keyCode = e.keyCode if (keyCode === 13) { // 阻止默认事件 e.preventDefault() // 判断支持遥控按钮 if (window.document.activeElement.classList.contains('remoteCls')) { // 触发点击事件 window.document.activeElement.click() } } } window.document.addEventListener('focus', (event) => { var tags = document.querySelectorAll('.remoteCls') for (var i = 0; i < tags.length; i++) { tags[i].classList.remove('remoteActive') } // 包含遥控按钮样式 if (window.document.activeElement.classList.contains('remoteCls')) { // 添加遥控选中效果 window.document.activeElement.classList.add('remoteActive') if (window.document.activeElement.classList.contains('menuButton') || window.document.activeElement.classList.contains('switchButton')) { // 关闭定时器1 this.$EventBus.$emit('closeTimer', 1) // 关闭定时器2 this.$EventBus.$emit('closeTimer', 2) } } }, true)
发表评论