It has been a while since we worked. It is necessary to understand the mechanism of event transmission. Recently, we studied it and recorded our experience.
1 Events in Android
There are many touch events in android. In the MotionEvent class of encapsulation, clicking, touching and sliding are our common events.
- MotionEvent.ACTION_DOWN
- MotionEvent.ACTION_MOVE
- MotionEvent.ACTION_UP
- MotionEvent.ACTION_CANCEL
2 Core Approach
Before we can understand the delivery mechanism, we need to learn a few ways.
2.1ViewGroup
- dispatchTouchEvent distributes events, accepts them by default and distributes them downward.If true is returned, events are intercepted and not distributed.The same below.
- The onInterceptTouchEvent preprocessing event returns false by default and true for interception
- OnTouchEvent handles events and returns false by default, without consuming events.If clickable=true is set in the root layout or code, super.onTouchEvent returns true, indicating that events were consumed.
2.2View
- dispatchTouchEvent
- onTouchEvent handles event return values by looking at the view property clickable==true
onTouchEvent return value for 2.3View/ViewGroup:
- super.onTouchEvent returns true if view/viewGroup is clickable, such as Button, clickable = true
- If view/viewGroup is not clickable, such as TextView, clickable = false, super.onTouchEvent returns false
- Setting the view/viewGroup clickable property to true in xml returns true for view:super.onTouchEvent
2.4 Core
- clickable affects the return value of super.onTouchEvent
- If false is returned, View can only handle down events, and subsequent events will not be triggered
- If true is returned, the view down\move\up can be triggered
3 Event Delivery
A complete screen touch event: finger press, slide, lift.This event consists of one action_down, several actions_moves, and one action_up.So how does it get across in our screens?
By default, event delivery is a process from Activity to ViewGroup to View, which is ultimately accepted by View.Figure:
We define a MyViewGroup and a MyView, and how to print them.
MyViewGroup, clickable false, super.onTouchEvent defaults to false, do not consume events
public class MyViewGroup extends FrameLayout { String Tag = "===MyViewGroup"; public MyViewGroup(Context context) { super(context); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: Log.e(Tag,"dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(Tag,"dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(Tag,"dispatchTouchEvent ACTION_UP"); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.e(Tag, "onInterceptTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(Tag, "onInterceptTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(Tag, "onInterceptTouchEvent ACTION_UP"); break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.e(Tag, "onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(Tag, "onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(Tag, "onTouchEvent ACTION_UP"); break; } return super.onTouchEvent(ev); } }
MyView inherits TextView, clickable defaults false, super.onTouchEvent defaults false, and does not consume events
public class MyView extends android.support.v7.widget.AppCompatTextView { String Tag = "===MyView"; public MyView(Context context) { super(context); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: Log.e(Tag,"dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(Tag,"dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(Tag,"dispatchTouchEvent ACTION_UP"); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: Log.e(Tag,"onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(Tag,"onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(Tag,"onTouchEvent ACTION_UP"); break; } return super.onTouchEvent(ev); } }
References in the active layout
<com.zcwipe.frecyclerviewdemo.MyViewGroup android:layout_width="match_parent" android:layout_height="wrap_content"> <com.zcwipe.frecyclerviewdemo.MyView android:layout_width="match_parent" android:layout_height="50dp" android:background="@color/color_theme" android:gravity="center" android:text="content" /> </com.zcwipe.frecyclerviewdemo.MyViewGroup>
Events start with action_down,
First activity executes the super.dispatchTouchEvent to distribute to the viewgroup.
Then viewgroup executes super.dispatchTouchEvent, super.onInterceptTouchEvent.
Finally view executes super.dispatchTouchEvent,onTouchEvent.This time the view receives the action_down event in onTouchEvent.

Subsequent action_move, action_up events are no longer passed to ViewGroup and View.Processed directly by activity.

If intercepted, the action_move of the viewgroup returns true, intercepting the event,
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.e(Tag, "onInterceptTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(Tag, "onInterceptTouchEvent ACTION_MOVE"); return true; case MotionEvent.ACTION_UP: Log.e(Tag, "onInterceptTouchEvent ACTION_UP"); break; } return super.onInterceptTouchEvent(ev); }
Action is handled by onTouchEvent of viewgroup
We have a requirement to accept action_move events in a custom viewgroup to achieve a sliding effect. What should we do?
As we know from scenario 1, if view down does not consume events, and viewgroup does not consume events, then this cannot be achieved.
As we know from scenario 2, if the view consumes the down event and the viewgroup does not intercept it, then it will not be possible.
There are two implementations:
* down consumption in view onTouchEvent, move intercept in view group:onInterceptTouchEvent
* Down does not consume in view onTouchEvent, down consumes in view group:onTouchEvent
Sometimes we don't know if the view consumes events, so use the following scenario to write robust line code, in the viewgroup
1. ontouchevent down consumer events
2. move interception in onInterceptTouchEvent
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.e(Tag, "onInterceptTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: return true; // break; case MotionEvent.ACTION_UP: Log.e(Tag, "onInterceptTouchEvent ACTION_UP"); break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.e(Tag, "onTouchEvent ACTION_DOWN"); return true; case MotionEvent.ACTION_MOVE: Log.e(Tag, "onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(Tag, "onTouchEvent ACTION_UP"); break; } return super.onTouchEvent(ev); }
This stabilizes the handling of action_move events in the viewgroup.