Article about the Android View event distribution process on the network can be found a large, here to post a code-based article, the author is also a cow: the Android event distribution mechanism completely parse, take you from the source point of view thoroughly understand (on).
Although the talk is very good, but after reading still feel a little smattering, so I spent some time from the source of research on the Android Touch event distribution process, the following content only personal understanding, if there are mistakes to point out.
Let's start with an example, and first rewrite a MyButton inheritance button with the following code:
public class MyButton extends Button {public MyButton (context context) {super (context); } public MyButton (context context, AttributeSet Attrs) {Super (context, attrs); } public MyButton (context context, AttributeSet attrs, int defstyleattr) {Super (context, attrs, defstyleattr); } @Override public boolean dispatchtouchevent (Motionevent event) {switch (event.getaction ()) { Case MOTIONEVENT.ACTION_DOWN:MYLOG.E ("Dispatchtouchevent====mybutton=====action_down"); Break Case MOTIONEVENT.ACTION_MOVE:MYLOG.E ("Dispatchtouchevent====mybutton=====action_move"); Break Case MOTIONEVENT.ACTION_UP:MYLOG.E ("dispatchtouchevent====mybutton=====action_up"); Break } return Super.dispatchtouchevent (event); } @Override public boolean ontouchevent (Motionevent event) {switch (event.getaction ()) { Case MOTIONEVENT.ACTION_DOWN:MYLOG.E ("Ontouchevent====mybutton=====action_down"); Break Case MOTIONEVENT.ACTION_MOVE:MYLOG.E ("Ontouchevent====mybutton=====action_move"); Break Case MOTIONEVENT.ACTION_UP:MYLOG.E ("ontouchevent====mybutton=====action_up"); Break } return Super.ontouchevent (event); }
The layout file is as follows:
<relativelayout xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:tools= "http// Schemas.android.com/tools " android:layout_width=" match_parent " android:layout_height=" Match_parent " android:paddingbottom= "@dimen/activity_vertical_margin" android:paddingleft= "@dimen/activity_ Horizontal_margin " android:paddingright=" @dimen/activity_horizontal_margin " android:paddingtop=" @dimen /activity_vertical_margin " tools:context=". Mainactivity "> <com.xjp.testtouchevent.mybutton android:id=" @+id/mybutton " android:layout_ Width= "Wrap_content" android:layout_height= "wrap_content" android:text= "test"/></relativelayout >
The test activity is as follows:
public class Mainactivity extends Actionbaractivity {private Button MyButton; @Override protected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate); Setcontentview (R.layout.activity_main); MyButton = (Button) Findviewbyid (R.id.mybutton); Mybutton.setontouchlistener (New View.ontouchlistener () {@Override public boolean onTouch (View V, Mo Tionevent event) {switch (event.getaction ()) {case Motionevent.action_down: MYLOG.E ("Ontouch====mybutton=====action_down"); Break Case MOTIONEVENT.ACTION_MOVE:MYLOG.E ("Ontouch====mybutton=====action_move"); Break Case MOTIONEVENT.ACTION_UP:MYLOG.E ("ontouch====mybutton=====action_up"); Break } return false; } }); Mybutton.setonclicklistener (New View.onclicklistener () {@Override public void OnClick (View v) { MYLOG.E ("Onclick====mybutton=====onclick"); } }); }}
Click the test button to print the results as follows:
We can visually see from the Print results, click button buttons The event distribution process is as follows dispatchtouchevent---->ontouch---->ontouchevent----->onclick. And if you're careful you'll notice that the onclick Click event is triggered after the Action_up event, why is that?? Now we don't. We are merely speculating on the outcome of the event distribution from the printout, and now we are analyzing from the source why this event distribution process is like this.
Event distribution starts with the Dispatchtouchevent method, so here we override the Dispatchtouchevent method and finally call the Super.dispatchtouchevent (event) method of the parent class. So let's see what the methods in the parent class do. Clicking into the Dispatchtouchevent method of the parent class finds that this method was found in the view class, and it's not surprising that all the controls ' parent classes are view. Here I posted the latest source code as follows:
public boolean dispatchtouchevent (Motionevent event) {Boolean result = false; if (minputeventconsistencyverifier! = null) {minputeventconsistencyverifier.ontouchevent (event, 0); } final int actionmasked = event.getactionmasked (); if (actionmasked = = Motionevent.action_down) {//Defensive cleanup for new gesture Stopnestedscroll (); } if (Onfiltertoucheventforsecurity (event)) {//noinspection simplifiableifstatement Listener Info li = mlistenerinfo; if (Li! = null && Li.montouchlistener! = null && (mviewflags & enabled_mask) = = ENA BLED && Li.mOnTouchListener.onTouch (this, event)) {result = true; } if (!result && ontouchevent (event)) {result = true; }} if (!result && minputeventconsistencyverifier! = null) { Minputeventconsistencyverifier.onunhandledevent (event, 0); }//Nested scrolls if this is the end of a gesture; Also cancel it if we tried an action_down but we didn ' t want the rest//of the gesture. if (actionmasked = = Motionevent.action_up | | actionmasked = = Motionevent.action_cancel | | (actionmasked = = Motionevent.action_down &&!result)) {Stopnestedscroll (); } return result; }
ignoring other unrelated code, we look directly at the 17--25 line. The key to the If judgment in line 17th isLi.mOnTouchListener.onTouch ( ThisThe return value of the event),This interface callback is the Mybutton.setontouchlistener event we wrote outside (Button's Ontouch event), in the Mainactivity code, wethe value returned by the Setontouchlistener isfalse, so in the source we can see -if the condition of the line is not established, then the condition is not established, result=falseTherefore, the source code of the first atLineifjudge the first condition, and continue to execute the second condition, namely ontouchevent. Let's jump into this method and see what's inside. Look at the following code:
public boolean ontouchevent (Motionevent event) {if ((viewflags & clickable) = = Clickable | | (ViewFlags & long_clickable) = = long_clickable) {switch (event.getaction ()) {case Motione Vent. Action_up:boolean prepressed = (mprivateflags & pflag_prepressed)! = 0; if ((Mprivateflags & pflag_pressed)! = 0 | | prepressed) {//Take focus if we don't have it ALR Eady and we should in//touch mode. Boolean focustaken = false; if (isfocusable () && isfocusableintouchmode () &&!isfocused ()) {Focustaken = R Equestfocus (); } if (prepressed) {//The button is being released before we actually showed it as pressed. Make it show the pressed State now (before scheduling the click) to ensure//the user sees it. Setpressed (True, x, y); if (!mhasperformedlongpress) {//This was a tap, so remove the Longpre SS Check Removelongpresscallback (); Only perform take click Actions if we were in the pressed state if (!focustaken) { Use a Runnable and post this rather than calling//PerformClick D irectly. This lets other visual state//of the view update before click Actions Start. if (Mperformclick = = null) {Mperformclick = new PerformClick (); } if (!post (Mperformclick)) { PerformClick (); }}} if (munsetpressedstate = = null) { Munsetpressedstate = new Unsetpressedstate (); } if (prepressed) {postdelayed (munsetpressedstate, Viewconfiguration.getpressedstateduration ()); } else if (!post (munsetpressedstate)) {//If the post failed, unpress right now Munsetpressedstate.run (); } removetapcallback (); } break; return true; } return false; }
Let's see what we've done here, ignoring the rest, and we're going to look at 37 lines straight.PerformClick (); method, jump in and continue to see, (Note: The PerformClick method here is executed inside the ACTION_UP gesture!!! )
public boolean PerformClick () { final boolean result; Final Listenerinfo li = mlistenerinfo; if (Li! = null && Li.monclicklistener! = null) { playsoundeffect (soundeffectconstants.click); Li.mOnClickListener.onClick (this); result = true; } else { result = false; } Sendaccessibilityevent (accessibilityevent.type_view_clicked); return result; }
Did you see that? Line 6thLi.mOnClickListener.onClick ( This); This interface callback is the onclick event of our button. So far, we've analyzed the button event distribution process from the source code.
Conclusion: dispatchtouchevent---->ontouch---->ontouchevent----->onclick. And if you are careful you will notice that the onclick Click event is triggered after all action_up events.
Now let's look at other things: when Ontouch returns True, the results are as follows:
Surprised to find that unexpectedly did not execute the onclick event, right???? If you read the above article carefully, you probably know why? or let's analyze it with you: The source code is as follows:
public boolean dispatchtouchevent (Motionevent event) {Boolean result = false; if (minputeventconsistencyverifier! = null) {minputeventconsistencyverifier.ontouchevent (event, 0); } final int actionmasked = event.getactionmasked (); if (actionmasked = = Motionevent.action_down) {//Defensive cleanup for new gesture Stopnestedscroll (); } if (Onfiltertoucheventforsecurity (event)) {//noinspection simplifiableifstatement Listener Info li = mlistenerinfo; if (Li! = null && Li.montouchlistener! = null && (mviewflags & enabled_mask) = = ENA BLED && Li.mOnTouchListener.onTouch (this, event)) {result = true; } if (!result && ontouchevent (event)) {result = true; }} if (!result && minputeventconsistencyverifier! = null) { Minputeventconsistencyverifier.onunhandledevent (event, 0); }//Nested scrolls if this is the end of a gesture; Also cancel it if we tried an action_down but we didn ' t want the rest//of the gesture. if (actionmasked = = Motionevent.action_up | | actionmasked = = Motionevent.action_cancel | | (actionmasked = = Motionevent.action_down &&!result)) {Stopnestedscroll (); } return result; }
As can be seen from line 17th, the condition is established, result=true; then the 23rd line if condition does not perform a second judgment at all, then the Ontouchevent method is not executed and the OnClick interface is not invoked, so the button The onclick event in Setonclicklistener is not performed.
Let's give a simple flowchart as follows
Therefore, the relationship between event distribution is: The Dispatchtouchevent method executes the Ontouch interface callback, and then determines whether the Ontouchevent method is executed based on the return value of the Ontouch method. The OnClick interface callback is executed in the Ontouchevent method.
Analyze Android View event distribution Dispatchtouchevent,ontouch,ontouchevent,onclick logic sequence process from source point of view (i)