Launcher3简单的实现workspace icon焦点控制

最近客户提出Launcher3需要使用遥控器上下左右选择图标和点击enter进入选中的app,这个功能如果没有作改动,直接使用安卓原生逻辑控制会出现无法跳页,不同组件的view无法跳过去的情况。首先由另一个同事实现,因为项目急,而且之前也没有过这方面的需求(带触摸屏的东西,用遥控器控制有点脱裤子放屁的感觉),搞了一个通宵,然后写出来的逻辑各种乱,总之就是通过计算icon的个数还有当前选中的位置移动到下一个icon上,使用view的selector实现,因为桌面带了hostsea,allapps按钮等,逻辑复杂且通用性差,给后续维护提高了成本,因此需要想办法简化这个逻辑,提高通用性,花了大半天时间研究了下。 最终使用如下逻辑,简单实现焦点控制。

1.首先遥控按键会通过系统转换成安卓定义的上下左右,这部分逻辑是mcu接受到遥控信号,向服务发送信息, 再由服务转换成安卓定义的键值向各个应用分发按键事件,我们直接通过安卓原生的方法来监听按键就好,这里我们主要用到的是

android获取当前焦点页面名称_安卓


这几个键值分别对应遥控上的方向键上下左右和确认键,一般来说我们可以通过adb来模拟这些按键,

adb shell input keyevent 19 ,后面的19对应键值,可以通过查看源码获取键值,或者网上查询也行。2.这些按键通过系统分发后,我们通过重写Activity的onKeyDown方法可以监听得到,

android获取当前焦点页面名称_安卓_02


但是有一些按键如Enter键会被系统处理,这个方法里监听不到,因此我们需要重写一个更早获得按键消息得方法dispatchKeyEvent,去处理按键逻辑

android获取当前焦点页面名称_安卓_03


可以看到这个方法的返回值为boolean,当我们返回true时,表示我们已经处理了此事件,系统将不会继续分发或者处理此次事件,所以我们就可以在这里实现自己的逻辑,然后return true,系统就不会继续处理这个事件。

3.写焦点控制逻辑之前,我们需要在xml布局文件里面给不需要获取焦点的控件加入 android:focusable=“false” 这个属性,那么寻找焦点的时候就可以跳过这个控件直接找到下一个可以获取焦点的临近控件,比如这次自己添加的时间显示控件TextView就不需要选中,因为代码属于公司,这里也不方便贴太多,所以这里还是选择文字描述 。

4.那万事具备,只欠东风了。 下面就贴一下焦点的处理逻辑代码,这里也是我自己研究了大半天弄出来的,希望大神们不要见笑,因为也没有比较深入的了解,所以大家有更好的方法也可以提出来共同进步。

private boolean handlerWorkSpaceFocus(KeyEvent event) {
        try {
            int keyCode = event.getKeyCode();
            View curfocusview = getWindow().getDecorView().findFocus();
            if (curfocusview == null) {
                return false;
            }
            int finddirection = 0;
            if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
                finddirection = View.FOCUS_UP;
            } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
                finddirection = View.FOCUS_DOWN;
            } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
                finddirection = View.FOCUS_LEFT;
            } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
                finddirection = View.FOCUS_RIGHT;
            }
            if (finddirection == 0) {
                return false;
            }
            View nextfocusview = curfocusview.focusSearch(finddirection);
            Logcat.d("curfocusview =" + curfocusview + ", nextfocusview =" + nextfocusview);
            if (nextfocusview instanceof BubbleTextView) {
                Logcat.d("next >>>" + ((BubbleTextView) nextfocusview).getText());
            }
            if (nextfocusview != null) {
                nextfocusview.requestFocus();
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

首先通过 View curfocusview = getWindow().getDecorView().findFocus();获取当前拥有focus的控件,如果控件为空,则表示当前还没有控件获取焦点,这里一般不需要处理,直接返回false,让系统需寻找第一个fucos控件, 如果有特殊需求,可以使用view.requestFocus();来申请焦点。
然后我们通过传入的方向来赋值finddirection 这个值,来决定下一个寻找focus控件的方向,再通过当前拥有焦点的控件寻找它各个方向上最邻近且能获取焦点的控件,再通过 requestFocus方法申请焦点,就可以跳入下一个控件了。

5.此次修改遇到的一些问题,比如使用原生的焦点灰色想换成别的颜色怎么修改,

android获取当前焦点页面名称_android获取当前焦点页面名称_04


在styles.xml中自己的style中加入colorControlHighlight属性可以自定义focus选中时高亮的颜色,如果不想使用默认的高亮的话,可以在自定义的控件里加入setDefaultFocusHighlightEnabled(false);//屏蔽默认高亮背景(8.0以后版本才能使用),如果不生效,可以重写view的onFocusChaged方法,直接把focused置为false传递给父类,这样view就不会去绘制focus时的高亮背景。

android获取当前焦点页面名称_控件_05