/// <summary> /// Transforms a motion event into the coordinate space of a particular child view, /// filters out irrelevant pointer ids, and overrides its action if necessary. /// </summary> /// <remarks> /// Transforms a motion event into the coordinate space of a particular child view, /// filters out irrelevant pointer ids, and overrides its action if necessary. /// If child is null, assumes the MotionEvent will be sent to this ViewGroup instead. /// </remarks> private bool dispatchTransformedTouchEvent(android.view.MotionEvent @event, bool cancel, android.view.View child, int desiredPointerIdBits) { bool handled; // Canceling motions is a special case. We don't need to perform any transformations // or filtering. The important part is the action, not the contents. int oldAction = @event.getAction(); if (cancel || oldAction == android.view.MotionEvent.ACTION_CANCEL) { @event.setAction(android.view.MotionEvent.ACTION_CANCEL); if (child == null) { handled = base.dispatchTouchEvent(@event); } else { handled = child.dispatchTouchEvent(@event); } @event.setAction(oldAction); return handled; } // Calculate the number of pointers to deliver. int oldPointerIdBits = @event.getPointerIdBits(); int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits; // If for some reason we ended up in an inconsistent state where it looks like we // might produce a motion event with no pointers in it, then drop the event. if (newPointerIdBits == 0) { return false; } // If the number of pointers is the same and we don't need to perform any fancy // irreversible transformations, then we can reuse the motion event for this // dispatch as long as we are careful to revert any changes we make. // Otherwise we need to make a copy. android.view.MotionEvent transformedEvent; if (newPointerIdBits == oldPointerIdBits) { if (child == null || child.hasIdentityMatrix()) { if (child == null) { handled = base.dispatchTouchEvent(@event); } else { float offsetX = mScrollX - child.mLeft; float offsetY = mScrollY - child.mTop; @event.offsetLocation(offsetX, offsetY); handled = child.dispatchTouchEvent(@event); @event.offsetLocation(-offsetX, -offsetY); } return handled; } transformedEvent = android.view.MotionEvent.obtain(@event); } else { transformedEvent = @event.split(newPointerIdBits); } // Perform any necessary transformations and dispatch. if (child == null) { handled = base.dispatchTouchEvent(transformedEvent); } else { float offsetX = mScrollX - child.mLeft; float offsetY = mScrollY - child.mTop; transformedEvent.offsetLocation(offsetX, offsetY); if (!child.hasIdentityMatrix()) { transformedEvent.transform(child.getInverseMatrix()); } handled = child.dispatchTouchEvent(transformedEvent); } // Done. transformedEvent.recycle(); return handled; }
/// <summary> /// Dispatches a generic pointer event to a child, taking into account /// transformations that apply to the child. /// </summary> /// <remarks> /// Dispatches a generic pointer event to a child, taking into account /// transformations that apply to the child. /// </remarks> /// <param name="event">The event to send.</param> /// <param name="child">The view to send the event to.</param> /// <returns> /// /// <code>true</code> /// if the child handled the event. /// </returns> private bool dispatchTransformedGenericPointerEvent(android.view.MotionEvent @event , android.view.View child) { float offsetX = mScrollX - child.mLeft; float offsetY = mScrollY - child.mTop; bool handled; if (!child.hasIdentityMatrix()) { android.view.MotionEvent transformedEvent = android.view.MotionEvent.obtain(@event ); transformedEvent.offsetLocation(offsetX, offsetY); transformedEvent.transform(child.getInverseMatrix()); handled = child.dispatchGenericMotionEvent(transformedEvent); transformedEvent.recycle(); } else { @event.offsetLocation(offsetX, offsetY); handled = child.dispatchGenericMotionEvent(@event); @event.offsetLocation(-offsetX, -offsetY); } return handled; }