Android事件分发

2017/07/26 Android

参考文章: http://www.jianshu.com/p/e99b5e8bd67b http://www.cnblogs.com/hnrainll/archive/2011/11/14/2248564.html http://blog.csdn.net/droidpioneer/article/details/6706695

##Android View ViewGroup 在说明Android的事件分发流程,首先需要了解Android界面中的View和ViewGroup。在Android中,视图控件大部分都可以分为两类,即View和ViewGroup。

从控件的视图架构来说,ViewGroup为View的父控件,但实际上ViewGroup却是View的子类,View的行为特征ViewGroup也具备,但同时布局Layout继承自ViewGroup,所以具备了一些其他特点。一般来说,UI界面不会直接使用View和ViewGroup,而是使用其派生类:

  • View派生的直接子类有:AnalogClock,ImageView,KeyboardView, ProgressBar,SurfaceView,TextView,ViewGroup,ViewStub View派生出的间接子类有:AbsListView,AbsSeekBar, AbsSpinner, AbsoluteLayout, AdapterView,AdapterViewAnimator, AdapterViewFlipper, AppWidgetHostView, AutoCompleteTextView,Button,CalendarView, CheckBox, CheckedTextView, Chronometer, CompoundButton,
  • ViewGroup派生出的直接子类有:AbsoluteLayout,AdapterView,FragmentBreadCrumbs,FrameLayout,LinearLayout,RelativeLayout,SlidingDra **ViewGroup派生出的间接子类有**:AbsListView,AbsSpinner, AdapterViewAnimator, AdapterViewFlipper, AppWidgetHostView, CalendarView, DatePicker, DialerFilter, ExpandableListView, Gallery, GestureOverlayView,GridView,HorizontalScrollView, ImageSwitcher,ListView,

在Android开发中,经常会遇到这些基本控件和布局实现不了我们的需求,所以,自定义控件就成了必不可少的需求。具体关于自定义View和ViewGroup,可参考如下两篇文章: http://blog.csdn.net/lmj623565791/article/details/24252901/ http://blog.csdn.net/lmj623565791/article/details/38339817/

Android 事件分发机制

Android事件的分发,一般涉及到Activity、ViewGroup、View,而触摸事件一般是从Activity开始接收,然后一层一层向子控件分发,并最终回流到父控件,成一个完整的U型结构,如下图:

下面通过这个图来分析Android的事件分发。 首先来看涉及到三个函数:

  • dispatchTouchEvent:事件分发函数,即该函数决定事件是否要传递到子控件或终止传递
  • onInterceptTouchEvent:拦截函数,该函数只有ViewGroup才具有,该事件即对父控件是否拦截,如拦截,则事件不往下传递,给到自己的touchEvent中
  • onTouchEvent:具体的事件处理函数,消费即终止该事件传递

从上图可以得出如下结论:

  • 对于dispatchTouchEvent和onTouchEvent,return true即终结事件传递,也就是我们所说的消费该事件
  • ViewGroup中要拦截事件给自己的onTouchEvent处理,需要onInterceptTouchEvent return true将事件拦截,否则传递到父类(子View)
  • 对于dispatchTouchEvent返回false,事件停止往子View传递,同时回流父view
  • 由于View没有onInterceptTouchEvent,所以view的dispatchTouchEvent默认实现(super)就是把事件分发给自己的onTouchEvent。
  • 要终结事件传递(消费)只能在dispatchTouchEvent和onTouchEvent中(后续ACTION_MOVE和ACTION_UP事件会根据两个不同的地方消费由不同的传递)

以上触碰事件仅针对ACTION_DOWN的情况,对于ACTION_MOVE和ACTION_UP,在后面会具体分析 需要注意区分父view和父类是不一样的

ACTION_MOVE 和 ACTION_UP

ACTION_MOVE和ACTION_UP在传递的过程中并不是和ACTION_DOWN 一样,当在ACTION_DOWN的时候返回了false的话,则后面的其他Action(如ACTION_MOVE和ACTION_UP就不会执行)。 ACTION_DOWN事件在哪个控件消费了(return true), 那么ACTION_MOVE和ACTION_UP就会从上往下(通过dispatchTouchEvent)做事件分发往下传,就只会传到这个控件,不会继续往下传,如果ACTION_DOWN事件是在dispatchTouchEvent消费,那么事件到此为止停止传递,如果ACTION_DOWN事件是在onTouchEvent消费的,那么会把ACTION_MOVE或ACTION_UP事件传给该控件的onTouchEvent处理并结束传递。

实例说明

自定义一个CustomView:

public class CustomView extends View{
    private Paint mPaint;
    public CustomView(Context context) {
        this(context,null);
    }

    public CustomView(Context context, AttributeSet attributeSet) {
        super(context,attributeSet);
        mPaint = new Paint();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("chris","CustomView onTouchEvent");
        return true;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d("chris","CustomView dispatchTouchEvent");
        return true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setColor(Color.YELLOW);
        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
    }
}

在自定义一个CustomViewGroup,这里简单的就继承于LinerLayout:

public class CustomViewGroup extends LinearLayout{
    public CustomViewGroup(Context context) {
        super(context);
    }

    public CustomViewGroup(Context context, AttributeSet attributeSet) {
        super(context,attributeSet);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed,left,top,right,bottom);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("chris","CustomViewGroup onTouchEvent");
        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        Log.d("chris","CustomViewGroup onInterceptTouchEvent");
        return false;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d("chris","CustomViewGroup dispatchTouchEvent");
        return false;
    }
}

主界面:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d("chris","MainActivity dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("chris","MainActivity onTouchEvent");
        return super.onTouchEvent(event);
    }
}

布局如下:

    <com.asus.mytouchevent.CustomViewGroup
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <com.asus.mytouchevent.CustomView
            android:layout_width="100dp"
            android:layout_height="100dp" />
    </com.asus.mytouchevent.CustomViewGroup>

显示效果如下为一个黄色矩形

事件分发输出log

下面通过log来验证上面所说: 1、MainActivity中dispatchTouchEvent return true or false,表示消费了此事件,输出如下:

2、MainActivity中dispatchTouchEvent return super,CustomViewGroup中dispatchTouchEvent return true,表示消费此事件,不传递,输出如下:

3、CustomViewGroup中dispatchTouchEvent return false,表示回传给父控件处理,输出如下:

4、CustomViewGroup中dispatchTouchEvent return super,onInterceptTouchEvent return true,表示传递给自己的touchEvent,onTouchEvent return true表示自己消费了不再传递,输出如下:

5、CustomViewGroup中dispatchTouchEvent return super,onInterceptTouchEvent return true,表示传递给自己的touchEvent,onTouchEvent return false or super,就是不消费,则传递给父控件处理。输出如下:

6、CustomViewGroup中dispatchTouchEvent return super,onInterceptTouchEvent return false or super,则表示不给自己的touchEvent消费(即不拦截),则会按原来的路线继续走,即传递给父类view,CustomView中dispatchTouchEvent return true消费此事件,不传递,输出如下:

7、CustomView中dispatchTouchEvent return super,回流整个过程,输出如下:

8、CustomView中dispatchTouchEvent return false,传递给父控件处理,输出如下:

ACTION_DOWM ACTION_MOVE & ACTION_UP 输出log

1、在MainActivity的dispatchTouchEvent return true消费该事件,输出如下:

2、在CustomViewGroup的dispatchTouchEvent return true消费该事件,输出如下:

3、在CustomView的dispatchTouchEvent return true消费该事件,输出如下:

4、在CustomView的onTouchEvent return true消费该事件,输出如下:

5、在CustomViewGroup的onTouchEvent return true消费该事件,输出如下:

6、在MainActivity的onTouchEvent return true消费该事件,输出如下:

Search

    Table of Contents