本篇是在上一篇的基础上讨论《Touch事件学习 1 点击事件》, 不过例子非常简单只是一个引子,走出事件学习的第一步也通常是学习Android开发第一个基础到的涉及事件的例子。现在接着来从Andorid的源码的角度分析一下点击事件的原理,这里的源码是基于Android 4.0(即Andorid 14)。
之前也编译过Android的源码生成zip文件并且刷到手机上,但是之后重装系统源码也没有保留全部格式化了,并且再完整下载Andorid源码比较耗时耗力,最重要的是也没有必要。这里使用Android 4.0是因为Android SDK Manager中可以很容易的通过勾选“Sources for Andorid SDK” 获取此版本的源码,因为其提供最低版本的是Android 4.0这里就基于这一版进行分析。如果想更省事一些也可以直接通过在线方式源码,地址是:http://grepcode.com/ 直接搜索android可以在线查看所有版本的Android源码并且可以进行比较,但我个人还是比较喜欢通过IDE查看本地代码。
1. 从上一篇文章的例子中可以看出,仅调用了一个方法即view.setOnClickListener,这里就从View类的setOnClickListener方法开始查看
/** * Register a callback to be invoked when this view is clicked. If this view is not * clickable, it becomes clickable. * * @param l The callback that will run * * @see #setClickable(boolean) */ public void setOnClickListener(OnClickListener l) { // 先判断是否可以点击 if (!isClickable()) { // 如果不能点击,这里重置为可点击 setClickable(true); } // 把内部类参数复制给内部变量 mOnClickListener = l; }
从上面看到了setOnClickListener会初始化mOnClickListener内部变量,那何处会调用mOnClickListener呢?通过在View类中查找可以发现
/** * Call this view's OnClickListener, if it is defined. * * @return True there was an assigned OnClickListener that was called, false * otherwise is returned. */ public boolean performClick() { // 发送一个给定的可访问性的点击事件类型。 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); if (mOnClickListener != null) { // 播放点击音效 playSoundEffect(SoundEffectConstants.CLICK); // *重点* 触发内部类中的onClick方法 mOnClickListener.onClick(this); return true; } return false; }
3. 何处的调用执行点击事件?
通过在View类中查找performClick可以发现onKeyUp 与 onTouchEvent 两处调用,如果有些Android基础的话可以看出onKeyUp通常是点击物理键(例如:后退键、Home键)时触发,而之前的例子是手指点击触发的所以排除onKeyUp,先下面仅看onTouchEvent
4. 点击事件的触发根源onTouchEvent
/** * Implement this method to handle touch screen motion events. * * @param event The motion event. * @return True if the event was handled, false otherwise. */ public boolean onTouchEvent(MotionEvent event) { ...... if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { case MotionEvent.ACTION_UP: ...... // 点击事件最终处理的地方 if (mPerformClick == null) { mPerformClick = new PerformClick(); } if (!post(mPerformClick)) { performClick(); } ...... } break; } return true; } return false; }
上面仅仅贴出的是onTouchEvent与点击事件关联的代码,因为此方法中代码太多,避免干扰仅贴出了核心的几行伪代码。可以得出一个简单的结论是在onTouchEvent中调用执行点击事件执行代码的。
上面是通过查看源码右下至上一步步查找,找出点击事件触发的地方,但是代码具体执行的时候肯定是由上至下来执行的,而且按照执行的流程来分析也比较容易理解,但是可能还有一个疑问什么地方调用并执行的onTouchEvent?发现并提出问题是一个很好的习惯的,但是这里先知道onTouchEvent是最终处理点击事件的地方就可以了,学习是一点点来的,之后会总结这里这个问题,现在这个问题先搁置一下。
现在从分析源码反向也是代码执行的流程看下
1. 如果手指点击屏幕,Android系统会先触发执行onTouchEvent方法
2. 从case MotionEvent.ACTION_UP 可以看出当手指抬起时触发performClick()方法
3. performClick方法中会调用mOnClickListener.onClick(this);
4. mOnClickListener变量是在setOnClickListener方法中进行初始化
5. 触发执行setOnClickListener内部类中的代码,按上一篇的例子是弹出“点击图片”文字
当然这里仅仅是简单说下点击事件流程,并没有详细展开介绍,目的是先为了引出所有事件处理的最终方法View.onTouchEvent.。
如果想多了解一些的话,可以看看上一篇文章提高的View提供的事件相关方方法
setOnClickListener setOnLongClickListener setOnTouchListener这里仅仅跟踪了setOnClickListener方法,有兴趣可以跟踪下其他几个方法都何处调用的。
如果比较懒一点不想自己查也没关系,上面三个方法最终都是在onTouchEvent中进行处理,所以此方法是Android处理所有事件的最终的方法,包括上面看到的点击事件、长按事件,包括之后会学习的各种手势(例如:滚动、Fling、轻触等)都是在此方法中进行手势的判断。
触摸事件学习系列文章详见:
《Android Touch事件学习系列汇总》