android开发中的OnTouch onClick onTouchEvent研究
来源:互联网 发布:织田信忠 知乎 编辑:程序博客网 时间:2024/06/10 11:38
今天面试的时候有人问到了这个问题,当时一下没想起来,想这回来就研究一下,所以自己写了一个小demo
这个是我的layout的布局
<pre name="code" class="java"></pre><pre name="code" class="java">
public class MyLayout extends LinearLayout implements View.OnClickListener, View.OnTouchListener { public MyLayout(Context context) { super(context); Log.d(TAG,"MyLayout init"); } public MyLayout(Context context, AttributeSet attrs) { super(context, attrs); setOnClickListener(this); setOnTouchListener(this); } @Override public void onClick(View v) { Log.d(TAG,"MyFrameLayout onClick"); } @Override public boolean onTouch(View v, MotionEvent event) { Log.d(TAG,"MyLayout onTouch"); return false; } @Override public boolean dispatchTouchEvent(MotionEvent event) { Log.d(TAG,"MyLayout dispatchTouchEvent"); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { Log.d(TAG,"MyLayout onTouchEvent"+event.getAction()); return super.onTouchEvent(event); }}
</pre><pre name="code" class="java">
这个是我的button布局
public class FButton extends Button implements View.OnClickListener, View.OnTouchListener { public FButton(Context context) { super(context); Log.d(TAG,"FButton init"); } public FButton(Context context, AttributeSet attrs) { super(context, attrs); setOnClickListener(this); setOnTouchListener(this); } @Override public boolean dispatchTouchEvent(MotionEvent event) { Log.d(TAG,"FButton dispatchTouchEvent"); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { Log.d(TAG,"FButton onTouchEvent=="+event.getAction()); return super.onTouchEvent(event); } @Override public void onClick(View v) { Log.d(TAG,"FButton onClick"); } @Override public boolean onTouch(View v, MotionEvent event) { Log.d(TAG,"FButton onTouch"); return false; }}
我在Acitivity的布局中引用了这两个布局
然后点击button,出现如下的log,可以看到如下的输出
我们从打印结果可以直观看到,点击Button按钮事件分发过程如下 dispatchTouchEvent---->onTouch---->onTouchEvent----->onClick。并且如果仔细的你会发现,都是在ACTION_UP事件之后才触发onClick点击事件,为什么会是这样??现在我们不得而知。我们仅仅是从打印结果推测事件分发的结论,现在我们从源码分析下这个事件分发流程为什么是这样子。
这个是View的 dispatchTouchEvent 方法的源码
public boolean dispatchTouchEvent(MotionEvent event) { // If the event should be handled by accessibility focus first. if (event.isTargetAccessibilityFocus()) { // We don't have focus or no virtual descendant has it, do not handle the event. if (!isAccessibilityFocusedViewOrHost()) { return false; } // We have focus and got the event, then use normal event dispatch. event.setTargetAccessibilityFocus(false); } boolean result = false; if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } final int actionMasked = event.getActionMasked(); if (actionMasked == MotionEvent.ACTION_DOWN) { // Defensive cleanup for new gesture stopNestedScroll(); } if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } if (!result && onTouchEvent(event)) { result = true; } } if (!result && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } // Clean up after nested scrolls if this is the end of a gesture; // also cancel it if we tried an ACTION_DOWN but we didn't want the rest // of the gesture. if (actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL || (actionMasked == MotionEvent.ACTION_DOWN && !result)) { stopNestedScroll(); } return result;}
关键在于li.mOnTouchListener.onTouch(this, event) 的返回值,这个接口回调就是我们外面写的myButton.setOnTouchListener事件(Button 的onTouch事件)
在MainActivity代码里,我们setOnTouchListener返回的值是false,所以在源码中我们可以看到 17行的条件不成立,那么条件不成立,result=false;
因此,源码的第23行if 判断第一个条件成立,继续执行第二个条件,也就是onTouchEvent。我们跳到这个方法里看看里面干啥了?看如下代码:
public boolean onTouchEvent(MotionEvent event) { if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { case MotionEvent.ACTION_UP: boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0; if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) { // take focus if we don't have it already and we should in // touch mode. boolean focusTaken = false; if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { focusTaken = requestFocus(); } if (prepressed) { // The button is being released before we actually // showed it as pressed. Make it show the pressed // state now (before scheduling the click) to ensure // the user sees it. setPressed(true, x, y); } if (!mHasPerformedLongPress) { // This is a tap, so remove the longpress check removeLongPressCallback(); // Only perform take click actions if we were in the pressed state if (!focusTaken) { // Use a Runnable and post this rather than calling // performClick directly. This lets other visual state // of the view update before click actions start. if (mPerformClick == null) { mPerformClick = new PerformClick(); } if (!post(mPerformClick)) { performClick(); } } } if (mUnsetPressedState == null) { mUnsetPressedState = new UnsetPressedState(); } if (prepressed) { postDelayed(mUnsetPressedState, ViewConfiguration.getPressedStateDuration()); } else if (!post(mUnsetPressedState)) { // If the post failed, unpress right now mUnsetPressedState.run(); } removeTapCallback(); } break; return true; } return false; }
在标红的方法里,执行了点击事件的回调public boolean performClick() { final boolean result; final ListenerInfo li = mListenerInfo; if (li != null && li.mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); result = true; } else { result = false; } sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); return result;}
到此为止,我们从源码分析了Button事件分发过程结论:dispatchTouchEvent---->onTouch---->onTouchEvent----->onClick。
并且如果仔细的你会发现,是在所有ACTION_UP事件之后才触发onClick点击事件。
我有一部分借鉴了别人的博客,再写上一些自己的心得体会,onTouch是绑定了监听器的方法,这个优先级别最高,如果在这个方法里返回了ture,后面的onTouchEvent与
onClick就不会执行。
因此,事件分发之间的关系是:dispatchTouchEvent方法中线执行 onTouch接口回调,然后根据onTouch方法的返回值判断是否执行onTouchEvent方法,onTouchEvent方法中执行了onClick接口回调。
</pre><pre name="code" class="java">
0 0
- android开发中的OnTouch onClick onTouchEvent研究
- android中OnClick,OnTouch,OnTouchEvent的传递及优先级研究
- OnTouch(),onTouchEvent(),onClick()的区别
- onClick、onTouch、onTouchEvent的调用
- Android事件传递dispatchTouchEvent,ontouch,onInterceptTouchEvent,onTouchEvent,onClick,onLongClick,
- Android OnTouchEvent和OnClick、OnLongClick、OnTouch、TouchDelegate关系
- Android 中 onTouch 和OnClick 冲突的处理(onTouchEvent返回true时与onclick冲突)
- android onTouch 与 onTouchEvent
- android ontouch onclick
- Android 触屏事件 OnTouch onClick onTouchEvent对于触屏事件的处理和分发
- Android触摸屏事件派发机制详解与源码分析一(View篇)onTouch,onClick,ontouchevent
- Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)dispatchtouchevent,ontouch,ontouchevent,onclick
- Android触摸屏事件派发机制详解与源码分析三(Activity篇)dispatchtouchevent,ontouch,ontouchevent,onclick
- Android - OnTouchEvent()和setOnTouchListener()中的OnTouch()方法的区别?
- Android开发——浅谈onInterceptTouchEvent、onTouchEvent与onTouch
- Android开发——浅谈onInterceptTouchEvent、onTouchEvent与onTouch
- Android中onInterceptTouchEvent(),Ontouch(),onTouchEvent()
- Android onTouch()和onTouchEvent()区别
- Linux下获取存储介质扇区大小
- js内存泄露
- java中的内部类
- OpenLiveWriter
- 欢迎使用CSDN-markdown编辑器
- android开发中的OnTouch onClick onTouchEvent研究
- web项目中的路径
- 合并两个有序单链表的递归方法
- 「静态数据成员」和「静态成员函数」
- debian升级内核
- poj3570 Fund Management
- PHP 命令模式
- SQL Server的基本操作
- 学习Shell编程不迷茫之一----基础了解