/** Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}. */ public static float mapCoordInSelfToDescendent(View descendant, View root, int[] coord) { ArrayList<View> ancestorChain = new ArrayList<View>(); float[] pt = {coord[0], coord[1]}; View v = descendant; while (v != root) { ancestorChain.add(v); v = (View) v.getParent(); } ancestorChain.add(root); float scale = 1.0f; Matrix inverse = new Matrix(); int count = ancestorChain.size(); for (int i = count - 1; i >= 0; i--) { View ancestor = ancestorChain.get(i); View next = i > 0 ? ancestorChain.get(i - 1) : null; pt[0] += ancestor.getScrollX(); pt[1] += ancestor.getScrollY(); if (next != null) { pt[0] -= next.getLeft(); pt[1] -= next.getTop(); next.getMatrix().invert(inverse); inverse.mapPoints(pt); scale *= next.getScaleX(); } } coord[0] = (int) Math.round(pt[0]); coord[1] = (int) Math.round(pt[1]); return scale; }
/** * Given a coordinate relative to the descendant, find the coordinate in a parent view's * coordinates. * * @param descendant The descendant to which the passed coordinate is relative. * @param root The root view to make the coordinates relative to. * @param coord The coordinate that we want mapped. * @param includeRootScroll Whether or not to account for the scroll of the descendant: sometimes * this is relevant as in a child's coordinates within the descendant. * @return The factor by which this descendant is scaled relative to this DragLayer. Caution this * scale factor is assumed to be equal in X and Y, and so if at any point this assumption * fails, we will need to return a pair of scale factors. */ public static float getDescendantCoordRelativeToParent( View descendant, View root, int[] coord, boolean includeRootScroll) { ArrayList<View> ancestorChain = new ArrayList<View>(); float[] pt = {coord[0], coord[1]}; View v = descendant; while (v != root && v != null) { ancestorChain.add(v); v = (View) v.getParent(); } ancestorChain.add(root); float scale = 1.0f; int count = ancestorChain.size(); for (int i = 0; i < count; i++) { View v0 = ancestorChain.get(i); // For TextViews, scroll has a meaning which relates to the text position // which is very strange... ignore the scroll. if (v0 != descendant || includeRootScroll) { pt[0] -= v0.getScrollX(); pt[1] -= v0.getScrollY(); } v0.getMatrix().mapPoints(pt); pt[0] += v0.getLeft(); pt[1] += v0.getTop(); scale *= v0.getScaleX(); } coord[0] = (int) Math.round(pt[0]); coord[1] = (int) Math.round(pt[1]); return scale; }
// invalidate a rectangle relative to the view's coordinate system all the way up the view // hierarchy @SuppressLint("NewApi") public static void invalidateGlobalRegion(View view, RectF childBounds) { // childBounds.offset(view.getTranslationX(), view.getTranslationY()); if (DEBUG_INVALIDATE) if (DebugLog.DEBUG) DebugLog.d(TAG, "-------------"); while (view.getParent() != null && view.getParent() instanceof View) { view = (View) view.getParent(); view.getMatrix().mapRect(childBounds); view.invalidate( (int) Math.floor(childBounds.left), (int) Math.floor(childBounds.top), (int) Math.ceil(childBounds.right), (int) Math.ceil(childBounds.bottom)); if (DebugLog.DEBUG) { DebugLog.v( TAG, "INVALIDATE(" + (int) Math.floor(childBounds.left) + "," + (int) Math.floor(childBounds.top) + "," + (int) Math.ceil(childBounds.right) + "," + (int) Math.ceil(childBounds.bottom)); } } }
private static void a(View paramView, RectF paramRectF) { while ((paramView.getParent() instanceof View)) { paramView = (View)paramView.getParent(); paramView.getMatrix().mapRect(paramRectF); paramView.invalidate((int)Math.floor(left), (int)Math.floor(top), (int)Math.ceil(right), (int)Math.ceil(bottom)); } }
/** * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's * coordinates. * * @param descendant The descendant to which the passed coordinate is relative. * @param coord The coordinate that we want mapped. * @return The factor by which this descendant is scaled relative to this DragLayer. Caution this * scale factor is assumed to be equal in X and Y, and so if at any point this assumption * fails, we will need to return a pair of scale factors. */ public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) { float scale = 1.0f; float[] pt = {coord[0], coord[1]}; descendant.getMatrix().mapPoints(pt); scale *= descendant.getScaleX(); pt[0] += descendant.getLeft(); pt[1] += descendant.getTop(); ViewParent viewParent = descendant.getParent(); while (viewParent instanceof View && viewParent != this) { final View view = (View) viewParent; view.getMatrix().mapPoints(pt); scale *= view.getScaleX(); pt[0] += view.getLeft() - view.getScrollX(); pt[1] += view.getTop() - view.getScrollY(); viewParent = view.getParent(); } coord[0] = (int) Math.round(pt[0]); coord[1] = (int) Math.round(pt[1]); return scale; }
@Override public boolean onInterceptTouchEvent(MotionEvent event) { if (mTopCard == null) { return false; } if (mGestureDetector.onTouchEvent(event)) { return true; } final int pointerIndex; final float x, y; final float dx, dy; switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: mTopCard.getHitRect(childRect); CardModel cardModel = (CardModel) getAdapter().getItem(getChildCount() - 1); if (cardModel.getOnClickListener() != null) { cardModel.getOnClickListener().OnClickListener(); } pointerIndex = event.getActionIndex(); x = event.getX(pointerIndex); y = event.getY(pointerIndex); if (!childRect.contains((int) x, (int) y)) { return false; } mLastTouchX = x; mLastTouchY = y; mActivePointerId = event.getPointerId(pointerIndex); break; case MotionEvent.ACTION_MOVE: pointerIndex = event.findPointerIndex(mActivePointerId); x = event.getX(pointerIndex); y = event.getY(pointerIndex); if (Math.abs(x - mLastTouchX) > mTouchSlop || Math.abs(y - mLastTouchY) > mTouchSlop) { float[] points = new float[] {x - mTopCard.getLeft(), y - mTopCard.getTop()}; mTopCard.getMatrix().invert(mMatrix); mMatrix.mapPoints(points); mTopCard.setPivotX(points[0]); mTopCard.setPivotY(points[1]); return true; } } return false; }
@Override public boolean onTouchEvent(MotionEvent event) { if (mTopCard == null) { return false; } if (mGestureDetector.onTouchEvent(event)) { return true; } // Log.d("Touch Event", MotionEvent.actionToString(event.getActionMasked()) + " "); final int pointerIndex; final float x, y; final float dx, dy; switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: mTopCard.getHitRect(childRect); pointerIndex = event.getActionIndex(); x = event.getX(pointerIndex); y = event.getY(pointerIndex); if (!childRect.contains((int) x, (int) y)) { return false; } mLastTouchX = x; mLastTouchY = y; mActivePointerId = event.getPointerId(pointerIndex); float[] points = new float[] {x - mTopCard.getLeft(), y - mTopCard.getTop()}; mTopCard.getMatrix().invert(mMatrix); mMatrix.mapPoints(points); mTopCard.setPivotX(points[0]); mTopCard.setPivotY(points[1]); break; case MotionEvent.ACTION_MOVE: pointerIndex = event.findPointerIndex(mActivePointerId); x = event.getX(pointerIndex); y = event.getY(pointerIndex); dx = x - mLastTouchX; dy = y - mLastTouchY; if (Math.abs(dx) > mTouchSlop || Math.abs(dy) > mTouchSlop) { mDragging = true; } if (!mDragging) { return true; } mTopCard.setTranslationX(mTopCard.getTranslationX() + dx); mTopCard.setTranslationY(mTopCard.getTranslationY() + dy); mTopCard.setRotation(40 * mTopCard.getTranslationX() / (getWidth() / 2.f)); mLastTouchX = x; mLastTouchY = y; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (!mDragging) { return true; } mDragging = false; mActivePointerId = INVALID_POINTER_ID; ValueAnimator animator = ObjectAnimator.ofPropertyValuesHolder( mTopCard, PropertyValuesHolder.ofFloat("translationX", 0), PropertyValuesHolder.ofFloat("translationY", 0), PropertyValuesHolder.ofFloat( "rotation", (float) Math.toDegrees( mRandom.nextGaussian() * DISORDERED_MAX_ROTATION_RADIANS)), PropertyValuesHolder.ofFloat("pivotX", mTopCard.getWidth() / 2.f), PropertyValuesHolder.ofFloat("pivotY", mTopCard.getHeight() / 2.f)) .setDuration(250); animator.setInterpolator(new AccelerateInterpolator()); animator.start(); break; case MotionEvent.ACTION_POINTER_UP: pointerIndex = event.getActionIndex(); final int pointerId = event.getPointerId(pointerIndex); if (pointerId == mActivePointerId) { final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mLastTouchX = event.getX(newPointerIndex); mLastTouchY = event.getY(newPointerIndex); mActivePointerId = event.getPointerId(newPointerIndex); } break; } return true; }