自定义控件之事件分发

来源:互联网 发布:现在做农村淘宝赚钱吗 编辑:程序博客网 时间:2024/04/27 19:09

android事件分发机制:

Android中与事件分发机制相关的方法有dispatchTouchEvent(MotionEvent ev),onInterceptTouchEvent(MotionEvent ev),onTouchEvent(MotionEvent event)。

**注意:**View对dispatchTouchEvent(MotionEvent ev)和onInterceptTouchEvent(MotionEvent ev)响应的前提是View可以向下传递事件,如果View已经是最小的控件了,那么它就没有dispatchTouchEvent(MotionEvent ev)和onInterceptTouchEvent(MotionEvent ev)了,只有onTouchEvent(MotionEvent event)。

public class TouchUtils {    public static String getTouchAction(int actionId){        String action = null;        switch (actionId){            case MotionEvent.ACTION_DOWN:                action = "MotionEvent.ACTION_DOWN";                break;            case MotionEvent.ACTION_MOVE:                action = "MotionEvent.ACTION_MOVE";                break;            case MotionEvent.ACTION_UP:                action = "MotionEvent.ACTION_UP";                break;            case MotionEvent.ACTION_CANCEL:                action = "MotionEvent.ACTION_CANCEL";                break;            default:break;        }        return action;    }}

自定义控件LFather:

public class LFather extends LinearLayout {    private static String TAG = "事件分发";    public LFather(Context context) {        super(context);    }    public LFather(Context context, AttributeSet attrs) {        super(context, attrs);    }    public LFather(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        Log.d(TAG,"LFather      |dispatchTouchEvent    -->"+ TouchUtils.getTouchAction(ev.getAction()));        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        Log.e(TAG,"LFather      |onInterceptTouchEvent -->"+TouchUtils.getTouchAction(ev.getAction()));        return super.onInterceptTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        Log.w(TAG,"LFather      |onTouchEvent          -->"+TouchUtils.getTouchAction(event.getAction()));        return super.onTouchEvent(event);    }}

自定义控件LChild:

public class LChild extends LinearLayout {    private static String TAG = "事件分发";    public LChild(Context context) {        super(context);    }    public LChild(Context context, AttributeSet attrs) {        super(context, attrs);    }    public LChild(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        Log.d(TAG,"LChild       |dispatchTouchEvent    -->"+ TouchUtils.getTouchAction(ev.getAction()));        return super.dispatchTouchEvent(ev);//        return true;    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        Log.e(TAG,"LChild       |onInterceptTouchEvent -->"+TouchUtils.getTouchAction(ev.getAction()));        return super.onInterceptTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        Log.w(TAG,"LChild       |onTouchEvent          -->"+TouchUtils.getTouchAction(event.getAction()));        return super.onTouchEvent(event);    }}

主函数MainActivity:

public class MainActivity extends AppCompatActivity {    private static String TAG = "事件分发";    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        Log.w(TAG,"MainActivity |onTouchEvent          -->"+ TouchUtils.getTouchAction(event.getAction()));        return super.onTouchEvent(event);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        Log.d(TAG,"MainActivity |dispatchTouchEvent    -->"+ TouchUtils.getTouchAction(ev.getAction()));        return super.dispatchTouchEvent(ev);    }}

布局:

<cn.centran.zx_event_distribution.view.LFather    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/rl"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:gravity="center"    tools:context="cn.centran.zx_event_distribution.MainActivity">        <cn.centran.zx_event_distribution.view.LChild            android:id="@+id/lc"            android:layout_width="match_parent"            android:layout_height="400dp"            android:background="#88ff0000"></cn.centran.zx_event_distribution.view.LChild></cn.centran.zx_event_distribution.view.LFather>

效果:

这里写图片描述

事件分发机制分析:

事件分发:dispatchTouchEvent(MotionEvent ev):

当事件发生时,Activity的dispatchTouchEvent(MotionEvent ev)会将事件传递给最外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法,并由该 View 的 dispatchTouchEvent(MotionEvent ev) 方法对事件进行分发。dispatchTouchEvent 的事件分发逻辑如下:

  • 如果返回true:表示事件给当前的View的dispatchTouchEvent进行消费,并停止向下分发。
  • 如果返回false,那么有两种情况:

    1. 如果事件是Activity分发过来的,那么该事件就会返回给Activity的onTouchEvent进行处理
    2. 如果事件是外层父控件分发过来的,那么该事件就会返回给该控件的onTouchEvent进行处理
  • 如果返回super.dispatchTouchEvent(ev),那么该事件是否需要拦截需要通过onInterceptTouchEvent(MotionEvent ev)返回值来判断。

案例1:

          | MainActivity | LFather  | LChild |

————- | ————- |——–
dispatchTouchEvent | super.dispatchTouchEvent(ev) |super.dispatchTouchEvent(ev)|super.dispatchTouchEvent(ev)
onInterceptTouchEvent | |super.onInterceptTouchEvent(ev)|super.onInterceptTouchEvent(ev)
onTouchEvent |super.onTouchEvent(event)|super.onTouchEvent(event)|super.onTouchEvent(event)

点击红色中心,运行效果:

分析:

点击事件传递给MainActivity的dispatchTouchEvent,因为它的返回值是super.onTouchEvent(event),所以它又将事件分给外层的LFather的dispatchTouchEvent,LFather的dispatchTouchEvent的返回值也是super.dispatchTouchEvent(ev),所以需要通过LFather的onInterceptTouchEvent的返回值来判断是否需要拦截,因为它的onInterceptTouchEvent返回值是super.onInterceptTouchEvent(ev),查看源码知道返回的是false,所以表示不拦截事件,事件向下传递给LChild的dispatchTouchEvent,因为它的返回值也是super.dispatchTouchEvent(ev),所以需要通过onInterceptTouchEvent来判断是否需要拦截,因为onInterceptTouchEvent的返回值也是系统默认的super.onInterceptTouchEvent(ev),所以不拦截该事件,因为LChild是最里层的控件了,所以事件无法向下传递了,那么它就会传递个LChild的onTouchEvent,但是onTouchEvent的返回值是系统默认的super.onTouchEvent(event),所以事件接着向上一级传递,再传递个它的上一层控件LFather的onTouchEvent,它的返回值也是系统默认的super.onTouchEvent(event),所以不拦截,接着传递给MainActivity的onTouchEvent,最后就是后续事件就会在MainActivity的dispatchTouchEvent的分发的时候直接分发给MainActivity的onTouchEvent,不会再向下传递了。

案例2:

          | MainActivity | LFather  | LChild |

————- | ————- |——–
dispatchTouchEvent | super.dispatchTouchEvent(ev) |true|super.dispatchTouchEvent(ev)
onInterceptTouchEvent | |super.onInterceptTouchEvent(ev)|super.onInterceptTouchEvent(ev)
onTouchEvent |super.onTouchEvent(event)|super.onTouchEvent(event)|super.onTouchEvent(event)

运行结果:

这里写图片描述

结果分析:

点击事件通过MainActivity的dispatchTouchEvent传递个外层的LFather的dispatchTouchEvent,因为它的返回值是true,表示事件被它消费掉,不会再传递了。后续事件也一样。

案例3:

          | MainActivity | LFather  | LChild |

————- | ————- |——–
dispatchTouchEvent | super.dispatchTouchEvent(ev) |super.dispatchTouchEvent(ev)|false
onInterceptTouchEvent | |super.onInterceptTouchEvent(ev)|super.onInterceptTouchEvent(ev)
onTouchEvent |super.onTouchEvent(event)|super.onTouchEvent(event)|super.onTouchEvent(event)

运行结果:

这里写图片描述

结果分析:

事件通过MainActivity的dispatchTouchEvent传递给外层的LFather的dispatchtouchEvent,因为它的返回值是super.dispatchTouchEvent(ev),然后需要通过onInterceptTouchEvent的返回值判断是否需要拦截该事件,又因为onInterceptTouchEvent的返回值是super.onInterceptTouchEvent(ev),表示不拦截,事件继续向下分发到LChild的dispatchTouchEvent上,因为它的返回值为false,表示事件不再向下分发了,同时也不对事件进行消费,那么就会将事件传递给它的上一层LFather的onTouchEvent处理,因为LFather的onTouchEvent的返回值是super.onTouchEvent(event),所以接着向上传递,一直到MainActivity的onTouchEvent上,后续的事件那么就会通过MainActivity的dispatchTouchEvent直接传递个MainActivity的onTouchEvent进行处理。

事件拦截:onInterceptTouchEvent(MotionEvent ev):

当dispatchTouchEvent的返回值是super.dispatchTouchEvent(ev)的时候,那么事件就会需要通过onInterceptTouchEvent的返回值来判断是否需要拦截该事件,这种情况有两种:

  • 当返回值是true:表示拦截该事件,并将事件传递个它所在View的onTouchEvent来处理。
  • 当返回值是false:表示不拦截该事件,并将事件传递给下一层的View的dispatchTouchEvent进行分发。注意返回值是super.onInterceptTouchEvent(ev),查看源码知道,就是返回false。

案例4:

          | MainActivity | LFather  | LChild |

————- | ————- |——–
dispatchTouchEvent | super.dispatchTouchEvent(ev) |super.dispatchTouchEvent(ev)|super.dispatchTouchEvent(ev)
onInterceptTouchEvent | |super.onInterceptTouchEvent(ev)|true
onTouchEvent |super.onTouchEvent(event)|super.onTouchEvent(event)|super.onTouchEvent(event)

运行结果:

这里写图片描述

结果分析:

事件通过MainActivity的dispatchTouchEvent分发传递给LFather的dispatchTouchEvent,因为LFather中的dispatchTouchEvent返回值是super.dispatchTouchEvent(ev),表示需要通过LFather中的onInterceptTouchEvent来判断是否需要拦截该事件,因为onInterceptTouchEvent的返回值是super.onInterceptTouchEvent(ev),表示不拦截该事件,事件被分发给LChild的dispatchToucEvent,因为LChild的dispatchToucEvent的返回值是super.dispatchTouchEvent(ev),所以需要LChild中的onInterceptTouchEvent返回值来判断是否需要拦截,因为它的返回值是true,表示拦截该事件,并将事件分给LChild中的onTouchEvent进行处理,但是onTouchEvent的返回值是super.onTouchEvent(event),表示没有对该事件进行处理,那么事件就会上传到上一层,最后MainActivity中的onTouchEvent进行处理,后续事件就会直接通过MainActivity的dispatchTouchEvent分发到MainActivity的onTouchEvent进行处理。

事件响应:onTouchEvent(MotionEvent event):

当事件传递到onTouchEvent,事件响应逻辑分为两种:

  • 当返回值为true:表示事件在此被消耗。
  • 当返回值为false:表示事件没有在此被消耗,那么事件就会继续向上传递,直到Activity中onTouchEvent。注意当返回值是super.onTouchEvent(event),查看源码知道它就相当于返回false。

案例5:

          | MainActivity | LFather  | LChild |

————- | ————- |——–
dispatchTouchEvent | super.dispatchTouchEvent(ev) |super.dispatchTouchEvent(ev)|super.dispatchTouchEvent(ev)
onInterceptTouchEvent | |super.onInterceptTouchEvent(ev)|true
onTouchEvent |super.onTouchEvent(event)|true|super.onTouchEvent(event)

运行结果:

这里写图片描述

结果分析:

当事件由MainActivity的dispatchTouchEvent分发给LFather的dispatchTouchEvent的时候,因为该dispatchTouchEvent的返回值为super.dispatchTouchEvent(ev),那么在LFather中需要查看onInterceptTouchEvent的返回值来判断是否需要拦截该事件,但是onInterceptTouchEvent的返回值为super.onInterceptTouchEvent(ev),表示不拦截该事件,那么事件就会继续向下传递给LChild的dispatchTouchEvent,因为该dispatchTouchEvent的返回值为super.dispatchTouchEvent(ev),那么在LChild中需要查看onInterceptTouchEvent的返回值来判断是否需要拦截该事件,但是onInterceptTouchEvent的返回值为true,表示拦截该事件,并把事件传递给LChild中onTouchEvent来进行处理,但是该onTouchEvent的返回值super.onTouchEvent(event),表示不处理,那么事件将向上传递给LFather的onTouchEvent,因为该onTouchEvent的返回值为true,表示拦截事件,那么事件在此被消耗,后续的事件则通过MainActivity的dispatchTouchEvent分发给LFather的dispatchTouchEvent,然后再将事件传递个LFather的onTouchEvent来处理。

原创粉丝点击