Android ViewGroup的事件分发分析(上)

最近在查阅Android View的事件分发相关的知识,经过从网上查找和自己查阅相关源码,最终以一个Demo的形势分析下ViewGroup事件分发相关的知识点(感谢鸿神Android ViewGroup事件分发机制)。下面进入正题。先自定义一个Button和一个ViewGroup见代码。
MyButton.java代码

public class MyButton extends android.support.v7.widget.AppCompatButton {
    private String TAG = getClass().getSimpleName();
    public MyButton(Context context) {
        this(context,null);
    }

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

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"onTouchEvent down");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"onTouchEvent up");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"onTouchEvent move");
                break;
        }

        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        
        //防止父控件拦截事件
        //getParent().requestDisallowInterceptTouchEvent(true);
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"dispatchTouchEvent down");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"dispatchTouchEvent up");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"dispatchTouchEvent move");
                break;
        }
        return super.dispatchTouchEvent(event);
    }
}

MyButton类主要就重写了dispatchTouchEvent和onTouchEvent方法,这两个方法就是View事件分法相关的主要方法。
Mylly.java

public class MyLly extends LinearLayout {
    private String TAG = getClass().getSimpleName();
    public MyLly(Context context) {
        super(context);
    }

    public MyLly(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyLly(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {

        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"LinearLayout dispatchTouchEvent down");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"LinearLayout dispatchTouchEvent up");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"LinearLayout dispatchTouchEvent move");
                break;
        }
        return super.dispatchTouchEvent(event);
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean result = false;
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:

                //拦截事件的向下传递,自身的onTouchEvent处理事件
                //result=true;
                Log.e(TAG,"LinearLayout onInterceptTouchEvent down");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"LinearLayout onInterceptTouchEvent up");
                break;
            case MotionEvent.ACTION_MOVE:

                Log.e(TAG,"LinearLayout onInterceptTouchEvent move");

                break;
        }
        return result;
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"LinearLayout onTouchEvent down");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"LinearLayout onTouchEvent up");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"LinearLayout onTouchEvent move");
                break;
        }
        return super.onTouchEvent(event);
    }

}

Mylly继承了LinearLayout是个ViewGroup,主要重写了dispatchTouchEvent,onInterceptTouchEvent和onTouchEvent方法,这第几个是方法是ViewGroup事件分法的相关方法。

下面请看MainActivity,具体的分析我们从这里开始。
public class MainActivity extends AppCompatActivity{
private String TAG = "MyButton";
    Button myButton;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //给Mylly设置onclick
        findViewById(R.id.lly).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this,"click",Toast.LENGTH_LONG).show();
            }
        });
        
        myButton = findViewById(R.id.btn);

        //给myButton设置OnClickListener
         myButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 Toast.makeText(MainActivity.this,"onClick",Toast.LENGTH_LONG).show();
             }
         });

        //给myButton设置OnLongClickListener
        myButton.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                Toast.makeText(MainActivity.this,"onLongClick",Toast.LENGTH_LONG).show();
                return false;
            }
        });
    }
    
}

xml文件




    

在MainActivity里我们主要做了三件事,

1,给MyLly根布局添加的点击事件
2,给button设置的点击事件
3,给button设置的长按事件
在这里我们先分析点击事件

请问程序运行起来后,我点击一下按钮会有什么样的效果呢,是执行了mylly的onClick还是执行了button的onClick还是两个都执行呢。我们一起看看效果吧。


gif5新文件.gif

最终是执行了Button的click事件,在看下logcat看看事件执行顺序从中找到答案吧。



从日志中可以看到事件的执行顺序为
ViewGroup的dispatchTouchEvent----->ViewGroup的onInterceptTouchEvent---->Button的dispatchTouchEvent---->Button的onTouchEvent。
首先我们查看ViewGroup的dispatchTouchEvent方法

查看25行当触发ACTION_DOWN的时候,会调用onInterceptTouchEvent获取是否被拦截,这里我们是false,然后在46行省略部分查找消费事件的Target View具体看源码。找到后在76行会调用dispatchTransformedTouchEvent,


image.png

也就是调用我们这个程序中Button的dispatchTouchEvent。到这里也就是这个程序为什么没有调用Mylly的onclick的答案了, 因为事件被传递到了目标子View中。我们打开Button的dispatchTouchEvent方法。


image.png

先是在30行调用了onTouch方法,如果设置了OntouchListener并且返回了true后面的事件也就不会执行了,这里我们没有为Button 设置OnTouchListener所以会执行34行,进入onTouchEvent方法。
我们直接说重点代码。
image.png

会开启一个延时100ms的Thread


image.png

的100ms后还会再开启一个500-100的延时线程
image.png

image.png

在这里会执行performLongClick这个是干什么的呢?进去看看就明白了。一层层进入最终到达这里。


image.png

也说是如果我们给Button设置了长按事件,这里就行调用。注意它的返回值是boolean类型。最后我们来看看ACTION_UP,看看代码
image.png

打开performClick其它就是执行了我们设置的OnClicklistener的onClick方法。

到此我们解决了为什么最终会调用Button的click方法。

你可能感兴趣的:(Android ViewGroup的事件分发分析(上))