Custom onTouchEvent touch feedback

Motionevent.action_up contains a combination of press + lift events, which can be identified as a click event, in response to the click event performClick()

class TouchView(context: Context? , attrs: AttributeSet?) : View(context, attrs) { override fun onTouchEvent(event: MotionEvent): Boolean {if(event.actionmasked == motionEvent.action_up){performClick()} //true indicates that the event is consumed, And click events in the upper layer of the layout are not consumed (called) return true; }}Copy the code

Return true

This can be interpreted as a flag of event ownership, only related to the DOWN event:

  • The yellow area overlaps with the green area, and the yellow, green, and gray areas all register click events
    • At this point, when the onTouchEvent in the yellow region returns true, the click events in the yellow region will be consumed, while the events in the green region and the gray region will not be consumed, i.e., will not respond
    • If the yellow field returns false, the event is consumed level by level, yellow -> green -> gray

Event. ActionMasked and event. The action

Event. ActionMasked can also be written as event. Action. It is recommended to use event

  • GetActionMasked () and getAction()?
    • Choose getActionMasked (). GetAction () isn’t accurate enough in the multi-touch era
    • So why is getAction() still used in some places (including the Android source code)?

Because their scenes don’t allow for multi-touch

  • POINTER_DOWN/POINTER_UP: multi-touch events
    • GetActionIndex (): method used for multi-touch
    • Read on for POINTER_DOWN, POINTER_UP, and getActionIndex()

View.ontouchevent () source logic

  • When the user presses ACTION_DOWN:
    • If not in the slide control, switch to the down state and register the long press timer
    • If in the slide control, switch to the pre-pressed state, and register the pressed timer
  • When entering the pressed state and move (ACTION_MOVE):
    • Redraw Ripple Effect
    • If you move out of range, self-mark this event as invalid and ignore subsequent events
  • When the user lifts (ACTION_UP):
    • If it is in down state and long press is not triggered, switch to up state and click event is triggered, and clear

    All state

    • If the long press has been triggered, switch to the lifted state and clear all states
  • When the event ends unexpectedly (ACTION_CANCEL):
    • Switch to the lifted state and clear all states

Custom ViewGroup touch feedback

  • In addition to overwriting onTouchEvent(), you also need to overwrite onInterceptTouchEvent(). If you overwrite onInterceptTouchEvent, you also need to overwrite onTouchEvent
  • OnInterceptTouchEvent () does not return true the first time, but only when intercepting is needed in any event (the default is false, only when intercepting is needed).
  • In onInterceptTouchEvent(), in addition to detecting the intercepting, you also need to prepare for the work after the intercepting (mainly the same logic as onTouchEvent() code)
  • Only views with child views have onIntercepttouchEvents, such as ViewGroup. Views without child views do not have onintercepttouchEvents, such as EditText
class TouchLayout(context: Context? , attrs: AttributeSet?) : ViewGroup(context, attrs) { override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { TODO("Not yet implemented") } override fun shouldDelayChildPressedState(): Boolean { return super.shouldDelayChildPressedState() } override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {/ / return super. OnInterceptTouchEvent (ev) / / handle events to intercept logic here, Val delta = ev. y-100 if (delta>100){return true}else{return false}} Override fun onTouchEvent(event: MotionEvent?) : Boolean {// Override event handling logic return super.onTouchEvent(event)}}Copy the code

Touch feedback flow

  • Activity.dispatchTouchEvent()

    • Recursive: ViewGroup (View). DispatchTouchEvent ()
      • ViewGroup.onInterceptTouchEvent()
      • child.dispatchTouchEvent()
      • super.dispatchTouchEvent()
      • View.onTouchEvent()
    • Activity.onTouchEvent()
    //1. View View.dispatchTouchEvent(); public boolean dispatchTouchEvent(MotionEvent event){ retrun onTouchEvent(); } //2. ViewGroup ViewGroup.dispatchTouchEvent(); public boolean dispatchtouchEvent(MotionEvent event){ boolean result; if(interceptTouchEvent()){ result = onTouchEvent(); }else{result = child View dispatchTouchEvent} return result; }Copy the code

View.dispatchTouchEvent()

  • If OnTouchListener is set, call onTouchListener.ontouch ()
    • Return true if OnTouchListener consumes the event
    • If OnTouchListener does not consume the event, continue calling its own onTouchEvent() and return the same result as onTouchEvent()
  • If OnTouchListener is not set, do the same

ViewGroup.dispatchTouchEvent()

  • If the user presses ACTION_DOWN for the first time, clear the TouchTargets and DISALLOW_INTERCEPT flags
  • Intercept processing
  • If the event is not intercepted and is not CANCEL and is DOWN or POINTER_DOWN, try assigning a pointer to the child View via TouchTarget; And if assigned to a new child View, call child.dispatchTouchEvent() to pass the event to the child View
  • Let’s see if there’s a TouchTarget
    • If not, call your own super.dispatchTouchEvent()
    • If so, call child.dispatchTouchEvent() to pass the event to the corresponding child View(if any).
  • If POINTER_UP, clear POINTER information from TouchTargets. If it is UP or CANCEL, the status is reset

TouchTarget

  • Action: Records which pointer(finger) is pressed on each child View
  • Structure: one-way linked list