Android 事件分发详解

一、建立事件分发Demo

我们都知道事件分发与3个对象有关,Activity Viewgroup ,View.
所以下表显示了他们之间与事件相关的方法:
这里写图片描述
可以看到,Activity与View是没有拦截方法的。这很容易理解,如果Activity刚分发出来就拦截,还不如不分发呢,对吧!如果View做拦截是不是多此一举呢?后面没谁了啊。View就是最后一个。那有人就问了,那为什么View还有分发方法呢?这就与源码扯上关系了。
为了能看到时间分发的流程,我们就得创建这3对象的子类,并重写相应的方法。代码如下:
首先建立一个EventView吧

package com.dx.demi.View;


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

/**
 * Created by demi on 16/12/6.
 */

public class EventView extends View {
    private Paint mPaint = new Paint();
    private float x = 0, y = 0;
    private TextView textView;

    public EventView(Context context) {
        this(context, null);
    }

    public EventView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public EventView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPaint.setColor(Color.GREEN);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(2);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        System.out.println("View onTouchEvent..." + event.getAction());
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                x = event.getX();
                y = event.getY();
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                x = event.getX();
                y = event.getY();
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                x = 0;
                y = 0;
                invalidate();
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        System.out.println("View dispatchTouchEvent..." + event.getAction());
        return super.dispatchTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawLine(x, 0, x, y, mPaint);
        canvas.drawLine(0, y, x, y, mPaint);
        if (textView != null) {
            textView.setText(getTextString());
        }
    }

    private String getTextString() {
        return "坐标 ( " + x + "," + y + " )";
    }

    public void setTextView(TextView textView) {
        this.textView = textView;
    }

}

然后,EventLinearLayout跟上

package com.dx.demi.View;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.LinearLayout;

/**
 * Created by demi on 16/12/8.
 */

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

    public EventLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public EventLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        System.out.println("LinearLayout onInterceptTouchEvent..." + event.getAction());
        return super.onInterceptTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        System.out.println("LinearLayout dispatchTouchEvent..." + event.getAction());
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        System.out.println("LinearLayout onTouchEvent..." + event.getAction());
        return super.onTouchEvent(event);
    }
}

接下来是EventActivity:

package com.dx.demi.activity;

import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.widget.TextView;

import com.dx.demi.R;
import com.dx.demi.View.EventView;

/**
 * Created by demi on 16/12/6.
 */

public class EventActivity extends Activity {
    private float x =0,y =0 ;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event);
        TextView tv = (TextView) findViewById(R.id.tv);
        EventView eventView = (EventView) findViewById(R.id.event);
        eventView.setTextView(tv);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        System.out.println("activty dispatchTouchEvent..."+event.getAction());
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        System.out.println("activty onTouchEvent..."+event.getAction());
        return super.onTouchEvent(event);
    }

}

然后布局文件:

<?xml version="1.0" encoding="utf-8"?>
<com.dx.demi.View.EventLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:layout_margin="10dp"/>
    <com.dx.demi.View.EventView
        android:id="@+id/event"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorAccent"/>

</com.dx.demi.View.EventLinearLayout>

代码比较简单,EventView 就做了一个功能,获取用户手指点击的坐标,画出x ,y 轴到这个坐标的线条。我在每个事件方法中都做了输出。demo建立完毕,接下来就开始列举情况,分析数据了。

二、列举情况,数据分析

这一共有7个方法,每个方法有3种情况,return true,return false,return super,一共有3的7次方种,如果我每一种情况都列举出来的话,那我要到何年何月才能完成这篇博文呢?我觉得,首先可以去除一些情况,其实,我们处理点击事件都是在View中和Viewgroup中处理的,最关注的是View的两个方法,和ViewGroup的三个方法。所以Activity完全可以不管,return super即可。而且在ViewGroup做拦截的时候,View的两个方法的返回值不起作用了。因为根本就不会运行这两个方法。
我列举了以下几种情况:
1.所有返回值为super()(默认);
这种情况就是,View不做处理,ViewGoup不做拦截。

这里写图片描述

可以看到,默认情况下,事件从activity分发到ViewGroup,ViewGroup不做拦截,到达View的dispatchTouchEvent(),随后到达View的onTouchEvent(),View默认不消耗事件,传递到ViewGroup的onTouchEvent(),ViewGroup也不消耗事件。传递到了Activity的onTouchEvent(),最终ACTION_DOWN,就这样结束。接下来可以看到,ACTION_UP直接从分发开始就到Activity的onTouchEvent(),就结束了。没有经过ViewGroup,和View。

2.ViewGroup做拦截(ViewGroup的 onInterceptTouchEvent返回值为true,onTouchEvent()返回值为true,其他super)

这里写图片描述

可以看到,在ViewGroup做了拦截之后,View就不会运行那两个方法了,所有处理在ViewGroup层了,而且ACTION_UP,事件也传递到了ViewGroup层,让ViewGroup做了处理。

3.View消耗事件(View 的 onTouchEvent()返回值为true,其他为super)
这里写图片描述
可以看到,View消耗了事件之后,ViewGroup和Activity不会去再运行OnTouchEvent()了。而且ACTION_UP,事件也传递到了View层,让View做了处理。

4.ViewGroup停止分发事件(dispatchTouchEvent()返回值为false,其他为super)

这里写图片描述

ViewGroup 不往下分发事件了,所以可以看到ViewGroup和View的onTouchEvent()方法都没调用。View的dispatchTouchEvent(),也没调用,但Activity的onTouchEvent()方法调用了。这说明了什么?只要dispatchTouchEvent()调用了,并返回super,onTouchEvent()就会调用。所以说父类的dispatchTouchEvent()中调用了onTouchEvent()。

这4种情况是比较普遍的,其他情况很少见,甚至是不会出现,如果出现了,那程序就有bug了.

三、查看源码

源码我就不带大家看了,解析源码的大神应有尽有,附上友情链接:
http://blog.csdn.net/mynameishuangshuai/article/details/52912641

四、总结

事件分发从Activity到ViewGroup再到View,中途如果返回了false ,或者true,没有返回super,就会终止事件的分发。事件处理是从View到ViewGroup再到Activity.只要前一个消耗了事件,后面的就不会处理事件了,事件拦截就只有ViewGroup了,拦截后,事件不会分发到View层。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值