1、概述

在android运用中,UI有时需求呼应鼠标的hover状况及scroll操作,也即鼠标的移动状况及鼠标的滚轮操作。

比如,在鼠标hover到某个控件时显示一个dialog或popupwindow来介绍功用以及操作鼠标滚轮时进行recyclerview的翻页等等。 一起,在大屏设备上,往往需求供给一个快捷键支撑快捷操作,提高功用体会,如ctrl+c、ctrl+v等。

经过对鼠标及外接键盘事情的支撑,能为android大屏操作设备带来必定的体会提高。

2、体系对hover事情及scroll事情的支撑

Generic motion events describe joystick movements, mouse hovers, track pad touches, scroll wheel movements and other input events.

经过文档描绘,generic motion events描绘了如操纵杆、鼠标hover、触控板及滚动等输入事情。

android体系在activity及view层面均供给了相应的回调来分发及处理hover及scroll事情。

在view中有如下三个办法

android 鼠标及外接键盘按键事件分发

图一 dispatchGenericMotionEvent办法

望文生义,dispatchGenericMotionEvent办法担任分发generic motion事情,其中间接调用了 onGenericMotionEvent或 mOnGenericMotionListener.onGenericMotion 。

android 鼠标及外接键盘按键事件分发

图二 onGenericMotionEvent办法

onGenericMotionEvent办法是具体处理motion事情的。 从on开头就能够看出,这是一个回调办法,在恰当的机遇,体系会调用 onGenericMotionEvent 办法来处理motion事情(如图一所述,该办法是在dispatchGenericMotionEvent办法中被调用的)。 一般对于hover或者scroll的处理逻辑都能够放在这儿。 该办法注释很翔实的描绘了该办法的用法,在实践开发过程中也能够不时的参考一下。

android 鼠标及外接键盘按键事件分发

图三 onHoverEvent办法

该办法是处理hover事情的一个回调,能够方便快捷的处理hover事情。 该办法也是在dispatchGenericMotionEvent分发办法中被调用的。当事情被识别为hover事情时会调用onHoverEvent办法。

在activity层面,体系也供给了dispatchGenericMotionEvent办法与onGenericMotionEvent办法。 dispatchGenericMotionEvent参与motion event事情的从顶层分发,假如activity所包括的view树中没有消费该motion事情的,则调用自身的onGenericMotionEvent办法。

3、hover及scroll事情的分发顺序

运用一个简单demo来学习及验证事情的分发顺序,该demo界面中包括一个ViewGroup及View方针,如下图所示。

android 鼠标及外接键盘按键事件分发

图四 demo演示

当移动鼠标时,打印的日志如下图(该日志是在activity、viewgroup、view均为消费hover事情前提下打印的)

android 鼠标及外接键盘按键事件分发

图五 hover事情日志

经过日志能够看出,hover事情与touch事情的原理相似。从activity分发到viewgroup,再到view,在view中假如没有消费,再一级级的往上抛。

在打印日志学习过程中发现,当view接纳到了hover_enter事情时,activity却仍是分发的hover_move事情。这是为啥呢,所以打断点调试了一下,发现在viewgroup中的dispatchHoverEvent办法中,有如下代码片段。

android 鼠标及外接键盘按键事件分发

图六 hover_enter事情改变逻辑

当view没有被hover过期,将hover_move事情修改为hover_enter事情。这便是activity接纳到hover_move事情,子view却收到hover_enter事情的原因。

经过修改demo中viewgroup、view的onGenericMotionEvent办法的返回值,能够验证事情是由上到下,在由下到上,只有某个层级的view消费了事情,该事情才不会再继续往上返回。

4、体系对于外接键盘按键事情的支撑

与鼠标事情相似,activity与view类均有dispatchKeyEvent担任分发按键事情。

与此一起,view额定供给了dispatchKeyEventPreIme办法,以便在输入法接纳事情前有机会处理一些逻辑。 该办法签名如下图,其办法注释也翔实的解说了其作用。

android 鼠标及外接键盘按键事件分发

图七 dispatchKeyEventPreIme办法

实践中,view的dispatchKeyEventPreIme办法会先于activity的dispatchKeyEvent回调。

在activity及view中一起供给了onKeyDown、onKeyUp及其相应的回调接口来处理具体的按键。 需求留心的是,这些办法的注释明确说了,软件盘的按键不必定会回调这些接口。所以软件盘的事情不要依靠这些接口。

5、运用增加快捷键遇到的问题

监听某个快捷键,然后在activity上弹出某个popwindow。可是,在开发过程中发现,当弹出了popupwindow后,activity及其包括的子view均收不到后续的事情了。这是为啥呢?

经过调试后发现,本来当popupwindow弹出后,事情均下发到popupwindow对应的窗口及子view了。

如下图是弹出popupwindow后的调用仓库

android 鼠标及外接键盘按键事件分发

图八 popupwindow事情分发调用栈

从日志能够看出,事情是发送到对应窗口的ViewRootImpl的,然后在由ViewRootImpl继续担任分发。 popupwindow是经过windowmanager增加到window的,其ViewRootImpl与Activity的ViewRootImpl不是同一个方针。故弹出popupwindow后,后续的事情都会分发到popupwindow所包括的view树中,而与Activity没啥联系。

如下图是直接分发到activity的调用仓库

android 鼠标及外接键盘按键事件分发

图九 activity事情分发调用栈

从日志能够看出,事情下发到了Activity的DecorView中,然后再层层分发各级子view。

6、根据window的事情分发

经过图八和图九可知,事情是分发到当前获取焦点的窗口上的。留心,在实践开发中要留心窗口是否有焦点(能够体系接口设置window是否获取焦点)

android 鼠标及外接键盘按键事件分发

7、关于焦点问题

1、事情能否分发到view,要害要有焦点。 在接纳按键事情时,源码中会自动让相关的视图获取焦点。 在实践开发中,有时焦点简单被抢占,故导致接纳不到相关的事情。 能够先经过如下命令

adb shell dumpsys window | findstr mFocusWindow

检查当前窗口是否是方针窗口初步判断。 假如是方针的窗口,那么事情必定会下发到该窗口,再结合断点调试,经过findFocus办法或isFocus等相关办法判断是否有焦点来确定问题原因。

在view的onwindowfocuschanged办法注释上也有相关提醒。

android 鼠标及外接键盘按键事件分发

要想获取按键事情,view所在的窗口和自身都要获取焦点才能够。

2、在项目中遇到过焦点被抢占的问题,此刻能够调用requestFocus办法,可是发现有时会失利。在stackoverflow上有答复建议经过handler或view post消息到主线程调用,实践验证可行。

8、参考

感谢以下作者的博客,讲的非常的详细,收获颇丰。

1、android按键事情分发 。

2、为什么popupwindow没有创立PhoneWindow方针?

3、android window机制

4、View.requestFocus聚集源码分析