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中有如下三个办法
图一 dispatchGenericMotionEvent办法
望文生义,dispatchGenericMotionEvent办法担任分发generic motion事情,其中间接调用了 onGenericMotionEvent或 mOnGenericMotionListener.onGenericMotion 。
图二 onGenericMotionEvent办法
onGenericMotionEvent办法是具体处理motion事情的。 从on开头就能够看出,这是一个回调办法,在恰当的机遇,体系会调用 onGenericMotionEvent 办法来处理motion事情(如图一所述,该办法是在dispatchGenericMotionEvent办法中被调用的)。 一般对于hover或者scroll的处理逻辑都能够放在这儿。 该办法注释很翔实的描绘了该办法的用法,在实践开发过程中也能够不时的参考一下。
图三 onHoverEvent办法
该办法是处理hover事情的一个回调,能够方便快捷的处理hover事情。 该办法也是在dispatchGenericMotionEvent分发办法中被调用的。当事情被识别为hover事情时会调用onHoverEvent办法。
在activity层面,体系也供给了dispatchGenericMotionEvent办法与onGenericMotionEvent办法。 dispatchGenericMotionEvent参与motion event事情的从顶层分发,假如activity所包括的view树中没有消费该motion事情的,则调用自身的onGenericMotionEvent办法。
3、hover及scroll事情的分发顺序
运用一个简单demo来学习及验证事情的分发顺序,该demo界面中包括一个ViewGroup及View方针,如下图所示。
图四 demo演示
当移动鼠标时,打印的日志如下图(该日志是在activity、viewgroup、view均为消费hover事情前提下打印的)
图五 hover事情日志
经过日志能够看出,hover事情与touch事情的原理相似。从activity分发到viewgroup,再到view,在view中假如没有消费,再一级级的往上抛。
在打印日志学习过程中发现,当view接纳到了hover_enter事情时,activity却仍是分发的hover_move事情。这是为啥呢,所以打断点调试了一下,发现在viewgroup中的dispatchHoverEvent办法中,有如下代码片段。
图六 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办法,以便在输入法接纳事情前有机会处理一些逻辑。 该办法签名如下图,其办法注释也翔实的解说了其作用。
图七 dispatchKeyEventPreIme办法
实践中,view的dispatchKeyEventPreIme办法会先于activity的dispatchKeyEvent回调。
在activity及view中一起供给了onKeyDown、onKeyUp及其相应的回调接口来处理具体的按键。 需求留心的是,这些办法的注释明确说了,软件盘的按键不必定会回调这些接口。所以软件盘的事情不要依靠这些接口。
5、运用增加快捷键遇到的问题
监听某个快捷键,然后在activity上弹出某个popwindow。可是,在开发过程中发现,当弹出了popupwindow后,activity及其包括的子view均收不到后续的事情了。这是为啥呢?
经过调试后发现,本来当popupwindow弹出后,事情均下发到popupwindow对应的窗口及子view了。
如下图是弹出popupwindow后的调用仓库
图八 popupwindow事情分发调用栈
从日志能够看出,事情是发送到对应窗口的ViewRootImpl的,然后在由ViewRootImpl继续担任分发。 popupwindow是经过windowmanager增加到window的,其ViewRootImpl与Activity的ViewRootImpl不是同一个方针。故弹出popupwindow后,后续的事情都会分发到popupwindow所包括的view树中,而与Activity没啥联系。
如下图是直接分发到activity的调用仓库
图九 activity事情分发调用栈
从日志能够看出,事情下发到了Activity的DecorView中,然后再层层分发各级子view。
6、根据window的事情分发
经过图八和图九可知,事情是分发到当前获取焦点的窗口上的。留心,在实践开发中要留心窗口是否有焦点(能够体系接口设置window是否获取焦点)
7、关于焦点问题
1、事情能否分发到view,要害要有焦点。 在接纳按键事情时,源码中会自动让相关的视图获取焦点。 在实践开发中,有时焦点简单被抢占,故导致接纳不到相关的事情。 能够先经过如下命令
adb shell dumpsys window | findstr mFocusWindow
检查当前窗口是否是方针窗口初步判断。 假如是方针的窗口,那么事情必定会下发到该窗口,再结合断点调试,经过findFocus办法或isFocus等相关办法判断是否有焦点来确定问题原因。
在view的onwindowfocuschanged办法注释上也有相关提醒。
要想获取按键事情,view所在的窗口和自身都要获取焦点才能够。
2、在项目中遇到过焦点被抢占的问题,此刻能够调用requestFocus办法,可是发现有时会失利。在stackoverflow上有答复建议经过handler或view post消息到主线程调用,实践验证可行。
8、参考
感谢以下作者的博客,讲的非常的详细,收获颇丰。
1、android按键事情分发 。
2、为什么popupwindow没有创立PhoneWindow方针?
3、android window机制
4、View.requestFocus聚集源码分析