前言
由于最近的项目需要运行在安卓智能电视上,除了正常的功能开发外,为了操作方便还需要对遥控器进行支持。这里记录对遥控器支持的主要思路。
原理支持
Android设备对遥控器的响应是通过分发KeyEvent的方式进行的,体现在界面上是focus焦点在view间的移动等。
因此,我们可以从两方面入手,一是拦截系统事件分发过程,二是借助系统提供的属性实现焦点移动。
具体实现
方案一
拦截系统事件分发过程对相应的KeyEvent分别进行处理。这种方案需要自己记录焦点位置管理焦点的移动等。可以通过重写dispatchKeyEvent、onKeyUp、onKeyDown等实现系统事件拦截,实践证明dispatchKeyEvent最合适,它处在事件分发过程靠前的位置可以保证所有的事件都能接收到,而onKeyUp、onKeyDown有些按键(比如确认键)是接收不到的。
示例代码
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
int keyCode = event.getKeyCode();
int action = event.getAction();
return handleKeyEvent(action, keyCode)||super.dispatchKeyEvent(event);
}
private boolean handleKeyEvent(int action, int keyCode) {
if (action != KeyEvent.ACTION_DOWN)
return false;
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
case KeyEvent.KEYCODE_DPAD_CENTER:
... //确定键enter
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
...//向下键
break;
case KeyEvent.KEYCODE_DPAD_UP:
...//向上键
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
...//向左键
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
...//向右键
break;
default:
break;
}
return false;
}
焦点逻辑需要自己处理,突出焦点可以通过为view设置实现了多种state的selector-drawable作为背景来实现。这里要注意下,遥控器按下、松开会出发两次事件,这两个事件只处理一个就好,所以上面代码对action做了过滤
if (action != KeyEvent.ACTION_DOWN){
return false;
}
如果我们已经对事件做了处理要返回true通知系统事件已经被消费掉了不需要继续处理,如果不是我们感兴趣的事件要返回false让系统继续处理。另外确定键(OK键或者方向键中间按键)的处理建议采用模拟点击的方式,比如调用view的performClick方法,这样能让系统自己走touch该view的分发过程,达到思路最简单,代码最少的目的。
该方案的好处是不管什么样的界面都可以通过代码控制,具有普适性。但是由于焦点控制需要自己维护逻辑可能相对复杂,如果界面不是通过代码生成而是相对固定的可以通过第二种方案来实现。
方案二
系统提供了一些焦点相关的属性,通过设置View的属性来实现焦点转移
android:focusable:设置一个控件能否获得焦点
android:background:设置作为背景的drawable实现焦点突出
android:nextFocusDown:定义下一个获得焦点的控件当按下键时
android:nextFocusUp:定义下一个获得焦点的控件当按上键时
android:nextFocusLeft:定义下一个获得焦点的控件当按左键时
android:nextFocusRight:定义下一个获得焦点的控件当按右键时
如果界面相对固定,都是在xml文件中事先定义好的,可以通过上面的属性来实现遥控器按键的响应。这种方案不能保证所有view都能响应,实践证明,xml中设置无效的可以通过requestFocus强制设置一个焦点到指定的view。
该方案不需要拦截事件分发过程,但要求界面固定,可以根据具体情况选择合适方案,个人推荐方案一。