Android review-View event system

Speaking of event distribution, we must first talk about MotionEvent. MotionEvent represents a series of operations of the finger on the screen, mainly including four events:

  • ACTION_DOWN: triggered when the finger touches the screen
  • ACTION_MOVE: triggered when the finger moves on the screen
  • ACTION_UP: Triggered when the finger leaves the screen
  • ACTION_CANCEL: This is a cancellation event, not artificial. (If we give the event to the lowest child View for consumption, we will set a non-interceptible flag for the parent View (ViewGroup), then subsequent events will not be processed by the parent View by default, but it does not mean that the parent View cannot be intercepted. , If the parent View intercepts this event at this time, then the parent View will distribute an ACTION_CANCEL event to the child View)

So we mainly pay attention to the first three events.


Then there are three methods of event distribution:

  • dispatchTouchEvent: The method of click event delivery. When the method returns true, it means that the event is consumed by the current view; if it returns false, it means that it is handed over to the onTouchEvent in the parent class for processing; if it returns to super.dispatchTouchEvent, it means that the event will continue to be dispatched.

  • onInterceptTouchEvent: Determine whether to intercept the event

    When the method returns true, it means that the event is intercepted and handed over to its own onTouchEvent method for consumption; if it returns false, it means that it is not intercepted and needs to be passed to the subview. If super.onTouchEvent(ev) is returned, this block is a bit troublesome, divided into two cases:

    • If the View has a child View and the child View is clicked, it will not be intercepted and will continue to be distributed to the child View for processing, which is equivalent to returning false
    • If the View has no child View or child View but does not click on the neutron View (ViewGroup is equivalent to a normal View), then the onTouchEvent response of the View is given, which is equivalent to returning true at this time.
  • onTouchEvent: handle the click event

    The relationship between these three methods can be expressed in pseudo-code as there is an if in the dispatchTouchEvent method. The judgment condition of if is onInterceptTouchEvent. If this method returns true, call onTouchEvent. If it returns false, let dispatchTouchEvent return false.

The last point is that the Android screen is composed of Activity, ViewGroup and View, so event distribution should start with Activity.

An Activity contains a Window object, and Window is implemented by PhoneWindow. PhoneWindow regards DecorView as the root View of the entire application window, and this DecorView divides the screen into two areas: one is TitleView, the other is ContentView, and what we usually write is displayed in ContentView.

Activity

The method of event distribution is the dispatchTouchEvent method of Activity. This method will enter the superDispatchTouchEvent of getWindow, and getWindow is the Wndow class, and the only implementation class of the window class is PhoneWindow, so the dispatchTOuchEvent method of PhoneWindow is actually called, and PhoneWindow is actually It is the superDispatchTouchEvent method of DecorView that is called again, and DecorView is a subclass of FrameLayout, and FrameLayout is a subclass of ViewGroup. At this time, the event is passed from the Activity layer to the ViewGroup layer. But if it is not intercepted, that is, if the activity's getWindow.dispatchTouchEvent method returns false, the activity's onTouchEvent method will be called. In this method, it is mainly to determine whether the click event is outside the boundary. If it is within the boundary, the consumption event returns true. If it is not within the boundary, give up the processing of time. Return false.

ViewGroup

In the ViewGroup, you first need to make a judgment on the event. If it is ACTION_DOWN or the first few events are intercepted by child Views, it is judged whether the non-intercepting flag is set. If it is not set, onInterceptTouchEvent is called to determine whether it needs to be intercepted. By default, it is not intercepted. . If the flag is set, it is directly determined not to intercept. If the ViewGroup intercepts ACTION_DOWN at the beginning, it is intercepted by default here. In other words, if he has intercepted the event, other events in this sequence will be intercepted by default, and will not go back and call onInterceptTouchEvent to judge. Finally, according to whether it is intercepted, it is judged whether the ViewGroup consumes the event itself or continues to distribute to the child View. If the event is not intercepted, it traverses the child View, and then calls their dispatchTouchEvent in turn. If it is intercepted, the dispatchTransformTouchEvent of the ViewGroup is called for processing, and the dispatchTouchEvent of its parent class is called internally.

Why is the traversal of child views in reverse order

Taking into account our usual living habits, when setting a click event for an area, it is generally set for the top view, rather than for the view covering it, traverse in reverse order, from the top view to the Go inside to find whether there is a View receiving event, which is in line with the daily experience. From a high probability point of view, if there is a View receiving event, reverse order is a way to find the View faster.

View

When an event is passed to the View, it will first determine whether the onTouchListener is set, and if it is, it will call onTouch to consume it. If onTouchEvent is not called to consume it, it will judge whether click, longclick, and contextlick are set in onTouchEvent. If so, go Consumption. In other words, execute onTouch first and execute onclick. If onTouch is executed first, then onCLick will not be taken care of for a long time.


Sliding conflict

There are three types of sliding conflicts in View, one is the conflict of internal and external layouts in the same direction, the other is the conflict of different directions, and the other is the nesting of the first two.

For scenario 1, for example, when the outside needs to slide left and right, when the left and right sliding occurs, let the outside intercept. When going up and down, let the inside slide.

For Scenario 2, he cannot judge based on the sliding angle, distance difference and speed difference, but he can generally find a breakthrough in his business. For example, the business stipulates that when in a certain state, the external View responds, and when in another state, the internal View responds. According to this rule, the sliding is handled accordingly.

Scenario 3 is the nesting of the two, as long as it is solved layer by layer.

There are generally two solutions to sliding conflicts, one is the external interception method and the other is the internal interception method.

For details, see the ScrollView nested ViewPager example in this article:

Nested ListView in ScrollView has no sliding conflict, but there will be display conflict, and only one line of ListView can be displayed. But the range of this row can slide normally.

problem

When will ACTION_CANCEL be triggered? Will the click event be triggered when the button is touched and then swiped to the outside to lift it, and will it be swiped back and lifted?

  • Generally, ACTION_CANCEL and ACTION_UP are regarded as the end of View event processing. If ACTION_UP or ACTION_MOVE is intercepted in the parent View, at the moment when the parent view intercepts the message for the first time, the parent view specifies that the child view does not accept subsequent messages, and the child view will receive the ACTION_CANCEL event.
  • If you touch a control, but it is not lifted on the area of ​​this control (moved to another place), action_cancel will appear.

The click event is intercepted, but I want to pass it to the View below, how do I do it?

Rewriting the requestDisallowInterceptTouchEvent() method of the subclass to return true will not execute the onInterceptTouchEvent() of the parent class, and the click event can be passed to the View below.

How the click event is triggered

We all know that if a touch event is registered for a control, a series of ACTION_DOWN, ACTION_MOVE, ACTION_UP and other events will be triggered every time it is clicked. It stands to reason that dispatchTouchEvent will be triggered every time an event. However, if the dispatchTouchEvent of an event in a group of events returns false, it means that this group of events has been processed, and the subsequent events will not trigger dispatchTouchEvent.

For example, the dispatchTouchEvent of ACTION_DOWN returns false, and the subsequent series of other actions will no longer be executed. Simply put, when dispatchTouchEvent is dispatching events, only the dispatchTouchEvent of the previous action returns true (here it means that dispatchTouchEvent returns true instead of onTouch returns true), then the latter action will be triggered. (The action and event here have the same meaning)

If a view is clickable or longclickable, then it will always consume the action_up event, consumed in onTouchEvent, and will not be passed to its parent

Action_Cancel mechanism

First look at an example:

There is a Button in the ViewGroup:

  1. Intercept the event in the VIewGroup, then an Action_Cancel will appear in the Button. If the ViewGroup intercepts the Move event, then the Move event will be converted to a Cancel event and passed to the child view
  2. If ViewGroup is not intercepted, Cancel will not appear, only move and up

A certain child View handles the Down event, then the subsequent Move and Up events will also be handed over to it for processing. But before it is processed, the parent View can still intercept the event. If the event is intercepted, the child View will receive a Cancel event and will not receive subsequent Move and Up events.

Guess you like

Origin blog.csdn.net/why1092576787/article/details/114906688