The Android event distribution mechanism is completely parsed to give you a thorough understanding of the source code (below)

Source: Internet
Author: User

Reprinted please indicate the source: http://blog.csdn.net/guolin_blog/article/details/9153761

In the previous article, I analyzed the event distribution mechanism of View in Android from the perspective of source code, I believe that I have a deep understanding of view event distribution.

If you have not yet read this article, please refer to the Android event distribution mechanism for complete parsing, so that you can fully understand the source code (I).

So today we will continue with the last unfinished topic, from the perspective of source code viewgruop event distribution.

First, let's discuss what is viewgroup? What is the difference between it and common view?

As the name suggests, viewgroup is a set of views. It contains many sub-views and sub-vewgroups. It is the parent class or indirect parent class for all layout in Android, like linearlayout and relativelayout are inherited from viewgroup. However, viewgroup is actually a view, but it has more features than view that can contain subview and define layout parameters. The viewgroup inheritance structure is as follows:

We can see that all the la s that we usually use in projects belong to the subclass of viewgroup.

After briefly introducing viewgroup, we now use a demo to demonstrate the vewgroup event distribution process in Android.

First, we define a layout named mylayout, inherited from linearlayout, as shown below:

public class MyLayout extends LinearLayout {public MyLayout(Context context, AttributeSet attrs) {super(context, attrs);}}

Then, open the main layout file activity_main.xml and add our custom layout to it:

<com.example.viewgrouptouchevent.MyLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/my_layout"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical" >    <Button        android:id="@+id/button1"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="Button1" />    <Button        android:id="@+id/button2"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="Button2" /></com.example.viewgrouptouchevent.MyLayout>

We can see that two buttons are added to mylayout, and the listening events are registered for both the buttons and mylayout in mainactivity:

myLayout.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {Log.d("TAG", "myLayout on touch");return false;}});button1.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Log.d("TAG", "You clicked button1");}});button2.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Log.d("TAG", "You clicked button2");}});

We printed a sentence in the ontouch method of mylayout and The onclick method of button1 and button2. Run the following project:

Click "button1", "button2", and "blank area" respectively. The output is as follows:

You will find that when you click the button, the ontouch method registered by mylayout will not be executed, and this method will be executed only when you click the blank area. You can first understand that The onclick method of the button consumes the event, so the event will not continue to be passed down.

It means that the touch event in Android is first transmitted to the view and then to the viewgroup? The conclusion is too early. Let's start an experiment.

You can see that viewgroup has an onintercepttouchevent method. Let's take a look at the source code of this method:

/** * Implement this method to intercept all touch screen motion events.  This * allows you to watch events as they are dispatched to your children, and * take ownership of the current gesture at any point. * * <p>Using this function takes some care, as it has a fairly complicated * interaction with {@link View#onTouchEvent(MotionEvent) * View.onTouchEvent(MotionEvent)}, and using it requires implementing * that method as well as this one in the correct way.  Events will be * received in the following order: * * <ol> * <li> You will receive the down event here. * <li> The down event will be handled either by a child of this view * group, or given to your own onTouchEvent() method to handle; this means * you should implement onTouchEvent() to return true, so you will * continue to see the rest of the gesture (instead of looking for * a parent view to handle it).  Also, by returning true from * onTouchEvent(), you will not receive any following * events in onInterceptTouchEvent() and all touch processing must * happen in onTouchEvent() like normal. * <li> For as long as you return false from this function, each following * event (up to and including the final up) will be delivered first here * and then to the target's onTouchEvent(). * <li> If you return true from here, you will not receive any * following events: the target view will receive the same event but * with the action {@link MotionEvent#ACTION_CANCEL}, and all further * events will be delivered to your onTouchEvent() method and no longer * appear here. * </ol> * * @param ev The motion event being dispatched down the hierarchy. * @return Return true to steal motion events from the children and have * them dispatched to this ViewGroup through onTouchEvent(). * The current target will receive an ACTION_CANCEL event, and no further * messages will be delivered here. */public boolean onInterceptTouchEvent(MotionEvent ev) {    return false;}

If you do not look at the source code, you may be scared by this comment, so long English comments are too big. But the source code is so simple! Only one line of code returns a false value!

Well, since it is a Boolean return, there are only two possibilities. We will re-write this method in mylayout and return a true value. The Code is as follows:

public class MyLayout extends LinearLayout {public MyLayout(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {return true;}}

Run the project again, and then run button1, button2, and the blank area respectively. The output is as follows:

You will find that, no matter where you click, the touch event of mylayout will always be triggered, and the button clicking event will be completely blocked! Why? If the touch event in Android is first transmitted to the view and then to the viewgroup, how can mylayout block the button click event?

It seems that only by reading the source code and figuring out the viewgroup event distribution mechanism in android can we solve our concerns, but here I 'd like to tell you first, in Android, the touch event is first transmitted to the viewgroup and then to the view. I remember that the Android event distribution mechanism is completely parsed, And I have explained from the perspective of the source code (I) that as long as you touch any control, the dispatchtouchevent method of the control will be called. This statement is true, but not complete. The actual situation is that when you click a control, the dispatchtouchevent method of the layout where the control is located will be called first, and then the corresponding control to be clicked will be found in the dispatchtouchevent method of the layout, then call the dispatchtouchevent method of the control. If we click the button in mylayout, The dispatchtouchevent method of mylayout will be called first, but you will find that this method is not used in mylayout. Then find it in its parent class linearlayout, and find that there is no such method. So we have to continue looking for the parent viewgroup of linearlayout. You finally saw this method in viewgroup. The dispatchtouchevent method of the button is called here. The modified information is as follows:

What are you waiting? Go and have a look at the source code of the dispatchtouchevent method in viewgroup! The Code is as follows:

public boolean dispatchTouchEvent(MotionEvent ev) {    final int action = ev.getAction();    final float xf = ev.getX();    final float yf = ev.getY();    final float scrolledXFloat = xf + mScrollX;    final float scrolledYFloat = yf + mScrollY;    final Rect frame = mTempRect;    boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;    if (action == MotionEvent.ACTION_DOWN) {        if (mMotionTarget != null) {            mMotionTarget = null;        }        if (disallowIntercept || !onInterceptTouchEvent(ev)) {            ev.setAction(MotionEvent.ACTION_DOWN);            final int scrolledXInt = (int) scrolledXFloat;            final int scrolledYInt = (int) scrolledYFloat;            final View[] children = mChildren;            final int count = mChildrenCount;            for (int i = count - 1; i >= 0; i--) {                final View child = children[i];                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE                        || child.getAnimation() != null) {                    child.getHitRect(frame);                    if (frame.contains(scrolledXInt, scrolledYInt)) {                        final float xc = scrolledXFloat - child.mLeft;                        final float yc = scrolledYFloat - child.mTop;                        ev.setLocation(xc, yc);                        child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;                        if (child.dispatchTouchEvent(ev))  {                            mMotionTarget = child;                            return true;                        }                    }                }            }        }    }    boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||            (action == MotionEvent.ACTION_CANCEL);    if (isUpOrCancel) {        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;    }    final View target = mMotionTarget;    if (target == null) {        ev.setLocation(xf, yf);        if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {            ev.setAction(MotionEvent.ACTION_CANCEL);            mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;        }        return super.dispatchTouchEvent(ev);    }    if (!disallowIntercept && onInterceptTouchEvent(ev)) {        final float xc = scrolledXFloat - (float) target.mLeft;        final float yc = scrolledYFloat - (float) target.mTop;        mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;        ev.setAction(MotionEvent.ACTION_CANCEL);        ev.setLocation(xc, yc);        if (!target.dispatchTouchEvent(ev)) {        }        mMotionTarget = null;        return true;    }    if (isUpOrCancel) {        mMotionTarget = null;    }    final float xc = scrolledXFloat - (float) target.mLeft;    final float yc = scrolledYFloat - (float) target.mTop;    ev.setLocation(xc, yc);    if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {        ev.setAction(MotionEvent.ACTION_CANCEL);        target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;        mMotionTarget = null;    }    return target.dispatchTouchEvent(ev);}

This method has a long code, so let's focus on it. First, you can see a condition judgment in row 13th. If disallowintercept and! If either onintercepttouchevent (EV) is true, the condition is determined. Disallowintercept indicates whether to disable event interception. The default value is false. You can also call the requestdisallowintercepttouchevent method to modify this value. When the first value is false, it depends entirely on the second value to determine whether it can enter the condition judgment. What is the second value? It turns out to be an inverse of the returned value of the onintercepttouchevent method! That is to say, if we return false in the onintercepttouchevent method, the second value will be set to true to enter the condition judgment. If we return true in the onintercepttouchevent method, the second value is set to false, thus jumping out of this condition.

Now you can think about it. Because we have just rewritten the onintercepttouchevent method in mylayout, so that this method returns true, all Button clicking events are blocked, then we have a reason to believe that the processing of button-click events is performed within the 13th row condition judgment!

Let's focus on how the internal conditions are implemented. In row 19th, A for loop is used to traverse all the sub-views in the current viewgroup, and then the row 24th is used to determine whether the view being traversed is the view being clicked, if yes, it will enter the internal judgment of the condition, and then the dispatchtouchevent of the view is called in row 29th. The subsequent process will be completely resolved with the Android event distribution mechanism, this gives you a thorough understanding of the source code (I. We also confirmed that the processing of button click events is indeed done here.

Note that the returned value is returned after the dispatchtouchevent of the sub-view is called. As we know, if a control can be clicked, the return value of dispatchtouchevent must be true when the control is clicked. Therefore, the condition judgment of the 29th rows is true. Therefore, the dispatchtouchevent method of the viewgroup is directly returned to the 31st rows. In this way, the subsequent Code cannot be executed, and the result printed by the demo above is also confirmed. If the Click Event of the button is executed, the touch event of mylayout will be intercepted.

What if we click a blank area instead of a button? In this case, the system will not return true In line 3, but will continue to execute the subsequent code. Then let's continue to look back. In row 44th, if the target is null, it will enter the condition to judge the interior. Generally, the target will be null here, therefore, super. dispatchtouchevent (EV ). Where can this code be called? Of course it is the dispatchtouchevent method in view, because the parent class of viewgroup is view. The subsequent processing logic is the same as previously mentioned, so the ontouch method registered in mylayout will be executed. The subsequent code is generally inaccessible, so we will not continue to analyze it.

Let's take a look at the process diagram of the entire viewgroup event distribution process. I believe this will help you better understand the process:

 

Now the analysis of the event distribution process of the entire viewgroup is over. Let's take a look at it.

1. Android event distribution is first transmitted to viewgroup and then to view by viewgroup.

2. in viewgroup, The onintercepttouchevent method can be used to intercept event transmission. The onintercepttouchevent method returns true, indicating that the event is not allowed to continue to be passed to the subview. If false is returned, the event is not blocked. The default value is false.

3.
If the sub-view consumes the passed events, the viewgroup will not be able to receive any events.

Now, the Android event distribution mechanism is completely resolved to this end. Combined with the last two articles, we believe that you have a profound understanding of event distribution.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.