public void recycle() { lock (sRecycleLock) { if (sRecycledCount < MAX_RECYCLED) { next = sRecycleBin; sRecycleBin = this; sRecycledCount += 1; } else { next = null; } child = null; } }
// all ones // The touched child view. // The combined bit mask of pointer ids for all pointers captured by the target. // The next target in the target list. public static android.view.ViewGroup.TouchTarget obtain(android.view.View child, int pointerIdBits) { android.view.ViewGroup.TouchTarget target; lock (sRecycleLock) { if (sRecycleBin == null) { target = new android.view.ViewGroup.TouchTarget(); } else { target = sRecycleBin; sRecycleBin = target.next; sRecycledCount--; target.next = null; } } target.child = child; target.pointerIdBits = pointerIdBits; return target; }
/// <summary>Removes the pointer ids from consideration.</summary> /// <remarks>Removes the pointer ids from consideration.</remarks> private void removePointersFromTouchTargets(int pointerIdBits) { android.view.ViewGroup.TouchTarget predecessor = null; android.view.ViewGroup.TouchTarget target = mFirstTouchTarget; while (target != null) { android.view.ViewGroup.TouchTarget next = target.next; if ((target.pointerIdBits & pointerIdBits) != 0) { target.pointerIdBits &= ~pointerIdBits; if (target.pointerIdBits == 0) { if (predecessor == null) { mFirstTouchTarget = next; } else { predecessor.next = next; } target.recycle(); target = next; continue; } } predecessor = target; target = next; } }
/// <summary>Adds a touch target for specified child to the beginning of the list.</summary> /// <remarks> /// Adds a touch target for specified child to the beginning of the list. /// Assumes the target child is not already present. /// </remarks> private android.view.ViewGroup.TouchTarget addTouchTarget(android.view.View child , int pointerIdBits) { android.view.ViewGroup.TouchTarget target = android.view.ViewGroup.TouchTarget.obtain (child, pointerIdBits); target.next = mFirstTouchTarget; mFirstTouchTarget = target; return target; }
/// <summary>Clears all touch targets.</summary> /// <remarks>Clears all touch targets.</remarks> private void clearTouchTargets() { android.view.ViewGroup.TouchTarget target = mFirstTouchTarget; if (target != null) { do { android.view.ViewGroup.TouchTarget next = target.next; target.recycle(); target = next; } while (target != null); mFirstTouchTarget = null; } }
public override bool dispatchTouchEvent(android.view.MotionEvent ev) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } bool handled = false; if (onFilterTouchEventForSecurity(ev)) { int action = ev.getAction(); int actionMasked = action & android.view.MotionEvent.ACTION_MASK; // Handle an initial down. if (actionMasked == android.view.MotionEvent.ACTION_DOWN) { // Throw away all previous state when starting a new touch gesture. // The framework may have dropped the up or cancel event for the previous gesture // due to an app switch, ANR, or some other state change. cancelAndClearTouchTargets(ev); resetTouchState(); } // Check for interception. bool intercepted; if (actionMasked == android.view.MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { bool disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action); } else { // restore action in case it was changed intercepted = false; } } else { // There are no touch targets and this action is not an initial down // so this view group continues to intercept touches. intercepted = true; } // Check for cancelation. bool canceled = resetCancelNextUpFlag(this) || actionMasked == android.view.MotionEvent .ACTION_CANCEL; // Update list of touch targets for pointer down, if needed. bool split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0; android.view.ViewGroup.TouchTarget newTouchTarget = null; bool alreadyDispatchedToNewTouchTarget = false; if (!canceled && !intercepted) { if (actionMasked == android.view.MotionEvent.ACTION_DOWN || (split && actionMasked == android.view.MotionEvent.ACTION_POINTER_DOWN) || actionMasked == android.view.MotionEvent .ACTION_HOVER_MOVE) { int actionIndex = ev.getActionIndex(); // always 0 for down int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) : android.view.ViewGroup .TouchTarget.ALL_POINTER_IDS; // Clean up earlier touch targets for this pointer id in case they // have become out of sync. removePointersFromTouchTargets(idBitsToAssign); int childrenCount = mChildrenCount; if (childrenCount != 0) { // Find a child that can receive the event. // Scan children from front to back. android.view.View[] children = mChildren; float x = ev.getX(actionIndex); float y = ev.getY(actionIndex); { for (int i = childrenCount - 1; i >= 0; i--) { android.view.View child = children[i]; if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child , null)) { continue; } newTouchTarget = getTouchTarget(child); if (newTouchTarget != null) { // Child is already receiving touch within its bounds. // Give it the new pointer in addition to the ones it is handling. newTouchTarget.pointerIdBits |= idBitsToAssign; break; } resetCancelNextUpFlag(child); if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. mLastTouchDownTime = ev.getDownTime(); mLastTouchDownIndex = i; mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; break; } } } } if (newTouchTarget == null && mFirstTouchTarget != null) { // Did not find a child to receive the event. // Assign the pointer to the least recently added target. newTouchTarget = mFirstTouchTarget; while (newTouchTarget.next != null) { newTouchTarget = newTouchTarget.next; } newTouchTarget.pointerIdBits |= idBitsToAssign; } } } // Dispatch to touch targets. if (mFirstTouchTarget == null) { // No touch targets so treat this as an ordinary view. handled = dispatchTransformedTouchEvent(ev, canceled, null, android.view.ViewGroup .TouchTarget.ALL_POINTER_IDS); } else { // Dispatch to touch targets, excluding the new touch target if we already // dispatched to it. Cancel touch targets if necessary. android.view.ViewGroup.TouchTarget predecessor = null; android.view.ViewGroup.TouchTarget target = mFirstTouchTarget; while (target != null) { android.view.ViewGroup.TouchTarget next = target.next; if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; } else { bool cancelChild = resetCancelNextUpFlag(target.child) || intercepted; if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits )) { handled = true; } if (cancelChild) { if (predecessor == null) { mFirstTouchTarget = next; } else { predecessor.next = next; } target.recycle(); target = next; continue; } } predecessor = target; target = next; } } // Update list of touch targets for pointer up or cancel, if needed. if (canceled || actionMasked == android.view.MotionEvent.ACTION_UP || actionMasked == android.view.MotionEvent.ACTION_HOVER_MOVE) { resetTouchState(); } else { if (split && actionMasked == android.view.MotionEvent.ACTION_POINTER_UP) { int actionIndex = ev.getActionIndex(); int idBitsToRemove = 1 << ev.getPointerId(actionIndex); removePointersFromTouchTargets(idBitsToRemove); } } } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1); } return handled; }