/// <summary>Maps a point to a position in the list.</summary> /// <remarks>Maps a point to a position in the list.</remarks> /// <param name="x">X in local coordinate</param> /// <param name="y">Y in local coordinate</param> /// <returns> /// The position of the item which contains the specified point, or /// <see cref="android.widget.AdapterView.INVALID_POSITION">android.widget.AdapterView.INVALID_POSITION /// </see> /// if the point does not intersect an item. /// </returns> public virtual int pointToPosition(int x, int y) { android.graphics.Rect frame = mTouchFrame; if (frame == null) { mTouchFrame = new android.graphics.Rect(); frame = mTouchFrame; } int count = getChildCount(); { for (int i = count - 1; i >= 0; i--) { android.view.View child = getChildAt(i); if (child.getVisibility() == android.view.View.VISIBLE) { child.getHitRect(frame); if (frame.contains(x, y)) { return(mFirstPosition + i); } } } } return(android.widget.AdapterView.INVALID_POSITION); }
/// <summary>Returns the View that should receive a touch at the given coordinates.</summary> /// <remarks>Returns the View that should receive a touch at the given coordinates.</remarks> /// <param name="rawX">The raw X.</param> /// <param name="rawY">The raw Y.</param> /// <returns>The view that should receive the touches, or null if there is not one.</returns> private android.view.View findViewForTouch(int rawX, int rawY) { // Reverse order so the child drawn on top gets first dibs. int containerCoordsX = rawX - mContainerRawLocation[0]; int containerCoordsY = rawY - mContainerRawLocation[1]; android.graphics.Rect frame = mTempRect; android.view.View closestChild = null; int closestChildDistanceSq = int.MaxValue; { for (int i = mContainer.getChildCount() - 1; i >= 0; i--) { android.view.View child = mContainer.getChildAt(i); if (child.getVisibility() != android.view.View.VISIBLE) { continue; } child.getHitRect(frame); if (frame.contains(containerCoordsX, containerCoordsY)) { return(child); } int distanceX; if (containerCoordsX >= frame.left && containerCoordsX <= frame.right) { distanceX = 0; } else { distanceX = System.Math.Min(System.Math.Abs(frame.left - containerCoordsX), System.Math.Abs (containerCoordsX - frame.right)); } int distanceY; if (containerCoordsY >= frame.top && containerCoordsY <= frame.bottom) { distanceY = 0; } else { distanceY = System.Math.Min(System.Math.Abs(frame.top - containerCoordsY), System.Math.Abs (containerCoordsY - frame.bottom)); } int distanceSq = distanceX * distanceX + distanceY * distanceY; if ((distanceSq < mTouchPaddingScaledSq) && (distanceSq < closestChildDistanceSq)) { closestChild = child; closestChildDistanceSq = distanceSq; } } } return(closestChild); }
/// <summary>Find the nearest touchable view to the specified view.</summary> /// <remarks>Find the nearest touchable view to the specified view.</remarks> /// <param name="root">The root of the tree in which to search</param> /// <param name="x">X coordinate from which to start the search</param> /// <param name="y">Y coordinate from which to start the search</param> /// <param name="direction">Direction to look</param> /// <param name="deltas"> /// Offset from the <x, y> to the edge of the nearest view. Note that this array /// may already be populated with values. /// </param> /// <returns>The nearest touchable view, or null if none exists.</returns> public virtual android.view.View findNearestTouchable(android.view.ViewGroup root , int x, int y, int direction, int[] deltas) { java.util.ArrayList <android.view.View> touchables = root.getTouchables(); int minDistance = int.MaxValue; android.view.View closest = null; int numTouchables = touchables.size(); int edgeSlop = android.view.ViewConfiguration.get(root.mContext).getScaledEdgeSlop (); android.graphics.Rect closestBounds = new android.graphics.Rect(); android.graphics.Rect touchableBounds = mOtherRect; { for (int i = 0; i < numTouchables; i++) { android.view.View touchable = touchables.get(i); // get visible bounds of other view in same coordinate system touchable.getDrawingRect(touchableBounds); root.offsetRectBetweenParentAndChild(touchable, touchableBounds, true, true); if (!isTouchCandidate(x, y, touchableBounds, direction)) { continue; } int distance = int.MaxValue; switch (direction) { case android.view.View.FOCUS_LEFT: { distance = x - touchableBounds.right + 1; break; } case android.view.View.FOCUS_RIGHT: { distance = touchableBounds.left; break; } case android.view.View.FOCUS_UP: { distance = y - touchableBounds.bottom + 1; break; } case android.view.View.FOCUS_DOWN: { distance = touchableBounds.top; break; } } if (distance < edgeSlop) { // Give preference to innermost views if (closest == null || closestBounds.contains(touchableBounds) || (!touchableBounds .contains(closestBounds) && distance < minDistance)) { minDistance = distance; closest = touchable; closestBounds.set(touchableBounds); switch (direction) { case android.view.View.FOCUS_LEFT: { deltas[0] = -distance; break; } case android.view.View.FOCUS_RIGHT: { deltas[0] = distance; break; } case android.view.View.FOCUS_UP: { deltas[1] = -distance; break; } case android.view.View.FOCUS_DOWN: { deltas[1] = distance; break; } } } } } } return(closest); }
/// <summary> /// Will forward touch events to the delegate view if the event is within the bounds /// specified in the constructor. /// </summary> /// <remarks> /// Will forward touch events to the delegate view if the event is within the bounds /// specified in the constructor. /// </remarks> /// <param name="event">The touch event to forward</param> /// <returns>True if the event was forwarded to the delegate, false otherwise.</returns> public virtual bool onTouchEvent(android.view.MotionEvent @event) { int x = (int)@event.getX(); int y = (int)@event.getY(); bool sendToDelegate = false; bool hit = true; bool handled = false; switch (@event.getAction()) { case android.view.MotionEvent.ACTION_DOWN: { android.graphics.Rect bounds = mBounds; if (bounds.contains(x, y)) { mDelegateTargeted = true; sendToDelegate = true; } break; } case android.view.MotionEvent.ACTION_UP: case android.view.MotionEvent.ACTION_MOVE: { sendToDelegate = mDelegateTargeted; if (sendToDelegate) { android.graphics.Rect slopBounds = mSlopBounds; if (!slopBounds.contains(x, y)) { hit = false; } } break; } case android.view.MotionEvent.ACTION_CANCEL: { sendToDelegate = mDelegateTargeted; mDelegateTargeted = false; break; } } if (sendToDelegate) { android.view.View delegateView = mDelegateView; if (hit) { // Offset event coordinates to be inside the target view @event.setLocation(delegateView.getWidth() / 2, delegateView.getHeight() / 2); } else { // Offset event coordinates to be outside the target view (in case it does // something like tracking pressed state) int slop = mSlop; @event.setLocation(-(slop * 2), -(slop * 2)); } handled = delegateView.dispatchTouchEvent(@event); } return(handled); }