View event delivery for Android
1. Basic knowledge
(1) All touch events are encapsulated into Motionevent objects, including the position of the touch, the time, the history, and the first few fingers (multi-touch).
(2) Event types are divided into Action_down, action_up, Action_move, Action_pointer_down, Action_pointer_up, Action_cancel, and each event is ACTION_ Down begins action_up end.
(3) The handling of events includes three classes, namely, the transfer of--dispatchtouchevent () function, intercepting--onintercepttouchevent () function, consumption--ontouchevent () function and Ontouchlistener
2. Transfer process
(1) The event is passed from Activity.dispatchtouchevent (), as long as it has not been stopped or intercepted, from the topmost view (ViewGroup) to the beginning of the Down (child View). The child View can handle the event by Ontouchevent ().
(2) The event is passed by the parent View (ViewGroup) to the child View,viewgroup can intercept the event by Onintercepttouchevent () and stop it from being passed down.
(3) If the event from the bottom of the transfer process has not been stopped, and the lowest level of the child View no consumption events, the event will be ViewGroup, when the Father view () can be consumed, if still not be consumed, the end will be to the activity of Ontouchevent () function.
(4) If the View does not consume Action_down, then other events will not be passed over.
(5) Ontouchlistener takes precedence over ontouchevent () to consume the event.
The above consumption means that the corresponding function return value is true.
Enclosed are two originals of the flowchart:
(1) View does not handle event flow chart
(2) View Handling Event flow chart
3, finally say a few words
Android Touch Events
Suppose the layout hierarchy is
Layout0
Layout1
Layout2
Layout3
If no one has gone to Intercepttouch, no one has handled the Ontouch event.
So layout0->intercept layout1->intercept layout2->intercept layout3->intercept
Layout3->ontouch Layout2->ontouch Layout1->ontouch Layout0->ontouch
Since no one has consumed Action_down events, subsequent Move,up events will not be passed in.
If Layout2 intercept, but not consumption ontouch
So layout0->intercept layout1->intercept layout2->intercept
Layout2->ontouch Layout1->ontouch Layout0->ontouch
Subsequent events are not passed in
If the Layout2 intercept, at the same time consumption.
So 0->intercept 1->intercept 2->intercept 2->ontouch
0->intercept 1->intercept 2->ontouch
0->intercept 1->intercept 2->ontouch
0->intercept 1->intercept 2->ontouch
If Layout2 intercept, do not consume, Layout1 consumption.
So 0->intercept 1->intercept 2->intercept
2->ontouch 1->ontouch
0->intercept 1->ontouch
0->intercept 1->ontouch
0->intercept 1->ontouch
Sum up. Law is
If the current layout intercept, then the child view and the son ViewGroup have no chance to get touch events. If the current layout does not consume events, the event will bubble up until a parent layout's ontouchevent consumes the event. If no parent layout consume this event, subsequent events will not be accepted.
If there is a layout consuming this event during the bubbling process. Then all the intercept of this layout's parent layout will still be invoked. But the current layout intercept will not be invoked again. Call the Ontouch event directly.
In addition, for the underlying view, there is a way to prevent the parent layer from view interception touch events, that is, to invoke GetParent (). Requestdisallowintercepttouchevent (True); Once the underlying view receives the touch action and calls this method then the parent layer view will not call onintercepttouchevent, and can not intercept the later action. In practice, it is found that ListView will call this method when scrolling. So that the action cannot be intercepted.
on Android View Touch event delivery problem
The project to achieve drag-and-drop function, that is, in the ScrollView inside the ImageView long press, there are shadows dragged away feeling. The realization of the idea is basically like this:
1, in the layout file put ImageView into the ScrollView
2, for ImageView registration touch monitoring
3, rewrite the ScrollView ontouchevent function.
4. Create a Popupwindow generated by a Imgeview
5. Update the location of the Popupwindow created by the move of touch. Realize the feeling that the picture on ImageView is towed away.
At the beginning, the ImageView responds to the touch event, but as the hand moves out the Imageview,touch event is not necessarily accepted by ImageView! Who should I pass to the touch event? Where did the touch incident slip from the ImageView? After trying to find out that the ImageView cancel event, the subsequent touch events were given to their parent operations, and here is ScrollView. The touch event ImageView the subsequent move events and up events directly to ScrollView after disappearing. We want to implement the effect of dragging the picture on the ImageView can be achieved by creating a Popupwindow, created Popupwindow only one picture. By updating the Popupwindow display position, you can drag and drop the picture on the ImageView.
Android's event-passing mechanism
The Android event-passing mechanism is divided into keystroke events and touch events, and events here refer to TouchEvent, the touch event.
A touchevent is generally composed of multiple motionevent (with Down,up,move,cancel four), which reasonably allocates these motionevent to the specified control to receive the corresponding TouchEvent, And then make a deal. For Motionevent please refer to another blog post I turned to.
I. Related classes and Methods
1. The class associated with the touch event is view,viewgroup,activity.
1 View here we represent classes that inherit from view and can no longer accommodate other controls, such as Textview,imageview. The following two methods are available in all three and are related to TouchEvent.
2 The ViewGroup here represent the classes that inherit from ViewGroup, and what they have in common is that they can continue to contain view. Like all kinds of layout and the disgusting ListView mentioned above.
3 The activity here represents the classes that inherit from the activity.
So there's no special description, and they all use a class to represent their entire group.
2. The way to relate to touch events is dispatchtouchevent,ontouchevent and Onintercepttouchevent,
public boolean dispatchtouchevent (Motionevent event)-for event distribution, three classes have this method
public boolean ontouchevent (Motionevent event)-for incident consumption, three classes have this method
public boolean onintercepttouchevent (motionevent ev)-for intercepting events, only ViewGroup has this method
The three methods are the same for use in three classes, but the detailed processing process is different. This we will explain in the next section.
two. The dispatchtouchevent process of motionevent
1.Activity Part
For normal understanding, it should be activity to get a certain motionevent and then start the event distribution, so let's look at the dispatchtouchevent source
public boolean dispatchtouchevent (motionevent ev) {
if (ev.getaction () = = Motionevent.action_down) {
Onuserinteraction ();
}
if (GetWindow (). superdispatchtouchevent (EV)) {return
true;
}
return ontouchevent (EV);
}
1 activity handling Events
for a mobile program, the first to get the gesture I think it should be window, So first you assign motionevent to the internal control of the activity with Superdispatchtouchevent, What superdispatchtouchevent actually do is framelayout.dispatchtouchevent (the process flow is like the viewgroup.dispatchtouchevent below) and it will look for any view that can handle the event 。 If the activity does not have an internal control or the internal control cannot handle the Motionevent, Superdispatchtouchevent returns false and then receives the motionevent is the activity itself, We can do detailed processing in the Ontouchevent method of activity.
2) activity does not handle events
just 1) says that the Superdispatchtouchevent returns False when the event does not have an internal control or the internal control cannot process the motionevent. But if there is an internal control that can handle the motionevent, it returns True, and the dispatchtouchevent of the activity returns true to tell the system that I have a control that receives the motionevent.
2.ViewGroup part
Superdispatchtouchevent distribute events. The first thing you receive is, of course, the layout control of the activity, which inherits from ViewGroup. When it is received, it is clear that the distribution of events will be considered first. Let's take a look at ViewGroup's dispatchtouchevent code
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) {// this is weird, we got a pen down, but we thought
It was// already down! xxx: we should probably send an action_up to the current/
/ target.
mmotiontarget = null; }// if we ' Re disallowing iNtercept or if we ' re allowing and we didn ' t// intercept if ( disallowintercept | | !onintercepttouchevent (EV)) {// reset this event ' s action (just to
protect ourselves) ev.setaction (Motionevent.action_down); we know we want to dispatch the event down, find a
child// who can handle it, start with the front-most child.
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)  ==&NBSp VISIBLE | | child.getanimation () != null) {child.gethitrect (frame); if (Frame.contains ( scrolledxint, scrolledyint)) {// offset the event to the view ' s
Coordinate system 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)) {// event handled, we have a target
now.
mmotiontarget = child;
return true;
}// the event didn ' T get handled, try the next view.
don ' t reset the event ' s location, it ' S not// necessary here.
}}}} boolean isuporcancel = (action == motionevent.action_up) | | (Action == motionevEnt.
Action_cancel); if (isuporcancel) {// note, we ' ve already copied the previous State to our local// variable, so this takes effect on the
next event mgroupflags &= ~flag_disallow_intercept; }// the event wasn ' t an action_down, dispatch it to our
Target if// we have one.
final view target = mmotiontarget; if (target == null) {// we don ' t have a target, this
Means we ' Re handling the// event as a regular view.
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 have&Nbsp;a target, see if we ' re allowed to and want to intercept its// events 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)) {// target didn ' T handle action_cancel.
not much we can do// but they should have.
}// clear the target mmotiontarget = null; don ' T dispatch this event to our own view, because we already// saw it when intercepting; we just want to giVe the following// event to the normal ontouchevent ().
return true; } if (Isuporcancel) {mmotiontarget = null;}// finally offset the event
to the target ' S coordinate system and// dispatch the event.
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); }
1) ViewGroup is treated as a normal view
The whole code means that there is a motionevent passed over, ViewGroup first look at their own internal view can deal with or say which view can be handled, the way to judge is to see if the motionevent is down, If it is, see if the view is visible, and if it is visible, see if the focus is not inside the view, and if it is inside, then we say we found a view that might process the motionevent and name it target (we know we want to dispatch The event down, get a child who can handle it, the start with the Front-most child.) If there is no view that can be handled (We don ' t have a target, This means we ' re handling the event as a regular view. ViewGroup will be treated as a normal view to this motionevent, then call Super.dispatchtouchevent (EV) method, this is the view of the dispatchtouchevent. Return statement returns the Super.dispatchtouchevent (EV)
2 ViewGroup internal view can be handled
If there is a view within the viewgroup that can be handled, assuming target, then the Target.dispatchtouchevent method is invoked. Return statement returns target.dispatchtouchevent (EV)
3) Onintercepttouchevent
Here's a very special way of onintercepttouchevent, which allows ViewGroup to intercept motionevent, meaning that we find that a target can get down focus, But ViewGroup does not want its internal view to handle the event, then intercepts it, at which point Dispatchtouchevent returns True.
3.View Part
The view section is relatively simple, After the target.dispatchtouchevent, Motionevent was passed to the dispatchtouchevent of view, and it should be understood that motionevent is also similar from activity to ViewGroup In the, In Superdispatchtouchevent framelayout.dispatchtouchevent found a view (actually a layout viewgroup) and called its dispatchtouchevent.
Look at the dispatchtouchevent in the View source code
public boolean dispatchtouchevent (Motionevent event) {
if montouchlistener!= null && (Mviewflags & Enab Led_mask = = ENABLED &&
montouchlistener.ontouch (this, event)) {return
true;
}
Return Ontouchevent (event);
}
Because view means that there are no other internal controls, there are only two options, which will first be handled using the Ontouchlistener defined by our developer, and if it can be handled, it will return true. If not, the default ontouchevent is used for processing.
three. The ontouchevent process of motionevent
The dispatchtouchevent of the bottom view will invoke Ontouchlistener to process the motionevent, or use ontouchevent to process the motionevent, whichever of which returns true by default. So then ViewGroup's dispatchtouchevent return value is true, so the dispatchtouchevent return value of the activity is true.
If we do not define our own ontouchlistener and rewrite the ontouchevent and return a false, then the dispatchtouchevent of ViewGroup returns to false. The activity will call its Ontouchevent method.
four. Subsequent motionevent
If Motionevent is down, the view is not processed, that is, it returns false within its dispatchtouchevent. Then the view's container ViewGroup no longer invoke the view's dispatchtouchevent, which means it will not be able to receive subsequent move,up. Only the down time is handled by view (returns true in Dispatchtouchevent) and subsequent move,up are passed to the view.
Five. The question of ListView
Want to give ListView realize the function of sliding page, normal is to use viewfillper, but do not viewflipper dynamic change adapter content and then refresh ListView words should how to achieve it. Have the following idea
1. Define a Gesture object Gesturedector for ListView, rewrite its ontouchevent, and use return gesturedector.ontouchevent inside. Gesturedector's gesture listener defaults to return false at Ondown, so a down motionevent is passed over, Ontouchevent returns false, according to the above argument, Dispatchtouchevent will also return false, and subsequent motionevent will no longer be passed to ListView, failing.
2. The Ondown return value of the override gesture listener is true, and the left and right pages can be implemented, but the onitemclicklistener of the ListView itself will not work properly. Failed.
3. Rewrite dispatchtouchevent, call super.dispatchtouchevent, and always return true. Rewrite Ontouchevent, The first call to Gesturedector.ontouchevent, if returned to false, that Gesturedector.ontouchevent did not handle the event, our left and right slide is not triggered, then return Super.ontouchevent processing, including its onitemclicklistener and so on, can run normally. Whatever value super.ontouchevent returns, because Dispatchtouchevent returns true, subsequent actions are transmitted. If returned to true, the gesturedector.ontouchevent handles the left and right sliding events (provided that the fling action returns true in the gesture listener), at this point return true. Success.
4. Rewrite dispatchtouchevent, start by calling Gesturedector.ontouchevent, and then the same way, and finally guaranteed to return true.