Android系统定制的导航侧边栏

Android手机的导航栏一般都放在底部,导航按键包括返回键、home键、最近任务键。而有些Android设备希望把导航栏放在左右两边,也就是改成侧边栏,这时候就需要二次定制开发。首先,把原生的底部导航栏屏蔽掉。然后,通过WindowManager添加悬浮的侧边栏,组合按键除了返回键、home键、最近任务键,还可以自定义添加其他按键。

为了响应按键点击事件,我们需要重写自定义View的onTouchEvent方法,监听ACTION_DOWN、ACTION_MOVE、ACTION_UP等动作,然后注入inputEvent事件,让系统响应对应键值的点击事件。通过查看KeyEvent源码,我们可知back键值、home键值:

    /** Key code constant: Home key.
     * This key is handled by the framework and is never delivered to applications. */
    public static final int KEYCODE_HOME            = 3;
    /** Key code constant: Back key. */
    public static final int KEYCODE_BACK            = 4;

注入inputEvent事件,需要构造KeyEvent对象,原方法是这样的:

 /**
     * Create a new key event.
     *
     * @param downTime The time (in {@link android.os.SystemClock#uptimeMillis})
     * at which this key code originally went down.
     * @param eventTime The time (in {@link android.os.SystemClock#uptimeMillis})
     * at which this event happened.
     * @param action Action code: either {@link #ACTION_DOWN},
     * {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}.
     * @param code The key code.
     * @param repeat A repeat count for down events (> 0 if this is after the
     * initial down) or event count for multiple events.
     * @param metaState Flags indicating which meta keys are currently pressed.
     * @param deviceId The device ID that generated the key event.
     * @param scancode Raw device scan code of the event.
     * @param flags The flags for this key event
     * @param source The input source such as {@link InputDevice#SOURCE_KEYBOARD}.
     */
    public KeyEvent(long downTime, long eventTime, int action,
                    int code, int repeat, int metaState,
                    int deviceId, int scancode, int flags, int source);

在onTouchEvent监听到Down、Up动作时,我们在这个时机注入:

    void sendEvent(int action, int flags, long when) {
        int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
        KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
                0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
                flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                InputDevice.SOURCE_KEYBOARD);
        //调用系统API注入inputEvent
        InputUtil.injectInputEvent(ev);
    }

系统API的注入inputEvent事件,只需要调用injectInputEvent方法:

        InputManager.getInstance().injectInputEvent(ev,InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);

而我们应用层API无法直接调用到,怎么办呢?大家应该都想起了反射,不错就是反射调用:

    public static void injectInputEvent(KeyEvent event) {
        try {
            Class<InputManager> inputManagerClass = InputManager.class;

            Method methodInject = inputManagerClass.getDeclaredMethod("injectInputEvent", InputEvent.class, int.class);
            methodInject.setAccessible(true);

            Method method = inputManagerClass.getDeclaredMethod("getInstance");
            method.setAccessible(true);
            InputManager instance = (InputManager) method.invoke(inputManagerClass);

            methodInject.invoke(instance, event, 0);
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

除了反射调用方法,还有另一种方法是,使用Instrumentation来发送按键事件。但是,需要在子线程执行,否则系统会抛异常:

    public static void doInject(final KeyEvent event){
        new Thread(new Runnable() {
            @Override
            public void run() {
                Instrumentation instrumentation = new Instrumentation();
                instrumentation.sendKeySync(event);
            }
        }).start();
    }

与back键、home键的事件注入不同的是,最近任务栏需要启动系统的任务栏Activity。通过查看系统源码,我们可以任务栏的包名是com.android.systemui,对应的Activity完整路径是com.android.systemui.recents.RecentsActivity。知道这些信息,我们就可以启动任务栏Activty:

	public void startRecentApp() {
		Intent intent = new Intent("com.android.systemui.recents.TOGGLE_RECENTS");
		ComponentName component = new ComponentName("com.android.systemui", 
				"com.android.systemui.recents.RecentsActivity");
		intent.setComponent(component);
		intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		try {
			startActivity(intent);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

至此,我们已经实现侧边栏的back键、home键、最近任务栏功能。当然,大家也可以自定义其他按键,实现酷炫的侧边栏。

发布了63 篇原创文章 · 获赞 179 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/u011686167/article/details/84134765