那么,如果我们的 View 已经进行了属性动画后,现在手指响应的触摸位置区域肯定不是 View 自己的left、right、top、bottom 这四个属性形成的区域了,但这个 View 却神奇的响应了我们的点击事件。
/** * Returns a MotionEvent that's been transformed into the child's local coordinates. * * It's the responsibility of the caller to recycle it once they're finished with it. * @param event The event to transform. * @param child The view whose coordinate space is to be used. * @return A copy of the the given MotionEvent, transformed into the given View's coordinate * space. */ private MotionEvent getTransformedMotionEvent(MotionEvent event, View child) { final float offsetX = mScrollX - child.mLeft; final float offsetY = mScrollY - child.mTop; final MotionEvent transformedEvent = MotionEvent.obtain(event); transformedEvent.offsetLocation(offsetX, offsetY); if (!child.hasIdentityMatrix()) { transformedEvent.transform(child.getInverseMatrix()); } return transformedEvent; } /** * Returns true if the transform matrix is the identity matrix. * Recomputes the matrix if necessary. * * @return True if the transform matrix is the identity matrix, false otherwise. */ final boolean hasIdentityMatrix() { return mRenderNode.hasIdentityMatrix(); } /** * Utility method to retrieve the inverse of the current mMatrix property. * We cache the matrix to avoid recalculating it when transform properties * have not changed. * * @return The inverse of the current matrix of this view. * @hide */ public final Matrix getInverseMatrix() { ensureTransformationInfo(); if (mTransformationInfo.mInverseMatrix == null) { mTransformationInfo.mInverseMatrix = new Matrix(); } final Matrix matrix = mTransformationInfo.mInverseMatrix; mRenderNode.getInverseMatrix(matrix); return matrix; }原来,ViewGroup 在 getTransformedMotionEvent() 方法中会通过子 View 的 hasIdentityMatrix() 方法来判断子 View 是否应用过位移、缩放、旋转之类的属性动画。如果应用过的话,那还会调用子 View 的 getInverseMatrix() 做「反平移」操作,然后再去判断处理后的触摸点是否在子 View 的边界范围内。
感叹,今天又发现了一些非常通用却被我们忽略掉的东西,不得不说,鸿洋的 wanandroid 带给了我们很多东西,更加惊叹的是「陈小缘」同学的 View 相关功底确实很强,这也难怪,他能写出如何有逼格的自定义 View 了。