// The hovered child view. // The next target in the target list. public static android.view.ViewGroup.HoverTarget obtain(android.view.View child) { android.view.ViewGroup.HoverTarget target; lock (sRecycleLock) { if (sRecycleBin == null) { target = new android.view.ViewGroup.HoverTarget(); } else { target = sRecycleBin; sRecycleBin = target.next; sRecycledCount--; target.next = null; } } target.child = child; return target; }
public void recycle() { lock (sRecycleLock) { if (sRecycledCount < MAX_RECYCLED) { next = sRecycleBin; sRecycleBin = this; sRecycledCount += 1; } else { next = null; } child = null; } }
protected internal override bool dispatchHoverEvent(android.view.MotionEvent @event ) { int action = @event.getAction(); // First check whether the view group wants to intercept the hover event. bool interceptHover = onInterceptHoverEvent(@event); @event.setAction(action); // restore action in case it was changed android.view.MotionEvent eventNoHistory = @event; bool handled = false; // Send events to the hovered children and build a new list of hover targets until // one is found that handles the event. android.view.ViewGroup.HoverTarget firstOldHoverTarget = mFirstHoverTarget; mFirstHoverTarget = null; if (!interceptHover && action != android.view.MotionEvent.ACTION_HOVER_EXIT) { float x = @event.getX(); float y = @event.getY(); int childrenCount = mChildrenCount; if (childrenCount != 0) { android.view.View[] children = mChildren; android.view.ViewGroup.HoverTarget lastHoverTarget = null; { for (int i = childrenCount - 1; i >= 0; i--) { android.view.View child = children[i]; if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child , null)) { continue; } // Obtain a hover target for this child. Dequeue it from the // old hover target list if the child was previously hovered. android.view.ViewGroup.HoverTarget hoverTarget = firstOldHoverTarget; bool wasHovered; { for (android.view.ViewGroup.HoverTarget predecessor = null; ; ) { if (hoverTarget == null) { hoverTarget = android.view.ViewGroup.HoverTarget.obtain(child); wasHovered = false; break; } if (hoverTarget.child == child) { if (predecessor != null) { predecessor.next = hoverTarget.next; } else { firstOldHoverTarget = hoverTarget.next; } hoverTarget.next = null; wasHovered = true; break; } predecessor = hoverTarget; hoverTarget = hoverTarget.next; } } // Enqueue the hover target onto the new hover target list. if (lastHoverTarget != null) { lastHoverTarget.next = hoverTarget; } else { lastHoverTarget = hoverTarget; mFirstHoverTarget = hoverTarget; } // Dispatch the event to the child. if (action == android.view.MotionEvent.ACTION_HOVER_ENTER) { if (!wasHovered) { // Send the enter as is. handled |= dispatchTransformedGenericPointerEvent(@event, child); } } else { // enter if (action == android.view.MotionEvent.ACTION_HOVER_MOVE) { if (!wasHovered) { // Synthesize an enter from a move. eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory); eventNoHistory.setAction(android.view.MotionEvent.ACTION_HOVER_ENTER); handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, child); // enter eventNoHistory.setAction(action); handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, child); } else { // move // Send the move as is. handled |= dispatchTransformedGenericPointerEvent(@event, child); } } } if (handled) { break; } } } } } // Send exit events to all previously hovered children that are no longer hovered. while (firstOldHoverTarget != null) { android.view.View child = firstOldHoverTarget.child; // Exit the old hovered child. if (action == android.view.MotionEvent.ACTION_HOVER_EXIT) { // Send the exit as is. handled |= dispatchTransformedGenericPointerEvent(@event, child); } else { // exit // Synthesize an exit from a move or enter. // Ignore the result because hover focus has moved to a different view. if (action == android.view.MotionEvent.ACTION_HOVER_MOVE) { dispatchTransformedGenericPointerEvent(@event, child); } // move eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory); eventNoHistory.setAction(android.view.MotionEvent.ACTION_HOVER_EXIT); dispatchTransformedGenericPointerEvent(eventNoHistory, child); // exit eventNoHistory.setAction(action); } android.view.ViewGroup.HoverTarget nextOldHoverTarget = firstOldHoverTarget.next; firstOldHoverTarget.recycle(); firstOldHoverTarget = nextOldHoverTarget; } // Send events to the view group itself if no children have handled it. bool newHoveredSelf = !handled; if (newHoveredSelf == mHoveredSelf) { if (newHoveredSelf) { // Send event to the view group as before. handled |= base.dispatchHoverEvent(@event); } } else { if (mHoveredSelf) { // Exit the view group. if (action == android.view.MotionEvent.ACTION_HOVER_EXIT) { // Send the exit as is. handled |= base.dispatchHoverEvent(@event); } else { // exit // Synthesize an exit from a move or enter. // Ignore the result because hover focus is moving to a different view. if (action == android.view.MotionEvent.ACTION_HOVER_MOVE) { base.dispatchHoverEvent(@event); } // move eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory); eventNoHistory.setAction(android.view.MotionEvent.ACTION_HOVER_EXIT); base.dispatchHoverEvent(eventNoHistory); // exit eventNoHistory.setAction(action); } mHoveredSelf = false; } if (newHoveredSelf) { // Enter the view group. if (action == android.view.MotionEvent.ACTION_HOVER_ENTER) { // Send the enter as is. handled |= base.dispatchHoverEvent(@event); // enter mHoveredSelf = true; } else { if (action == android.view.MotionEvent.ACTION_HOVER_MOVE) { // Synthesize an enter from a move. eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory); eventNoHistory.setAction(android.view.MotionEvent.ACTION_HOVER_ENTER); handled |= base.dispatchHoverEvent(eventNoHistory); // enter eventNoHistory.setAction(action); handled |= base.dispatchHoverEvent(eventNoHistory); // move mHoveredSelf = true; } } } } // Recycle the copy of the event that we made. if (eventNoHistory != @event) { eventNoHistory.recycle(); } // Done. return handled; }