為什么屬性動畫可以響應觸摸事件
關于Android 的動畫及原理,可以參考:
屬性動畫與 VIew動畫的關鍵區別之一,就是 屬性動畫 view 可以在動畫過程中收到點擊事件,而 view 動畫過程中,點擊事件仍在原地;基于此,很多人認為屬性動畫改變了 view 的屬性,導致view 的位置發生了變化,所以屬性動畫可以收到點擊事件,這種說法其實是錯誤的;
為什么如此說呢,我們這里給一個例子:
ObjectAnimator.ofFloat(v, "translationX", 0, 1000f)
.setDuration(1000)
.start();
在開發者選項中,我們打開顯示布局位置:

運行如下:

發現上面顯示的 view 的位置并沒有發生改變;
那么上面動畫到底做了什么呢,我們都知道,上面動畫會調用 View 的 setTranslationX 方法,我們來看源碼:
public void setTranslationX(float translationX) {
if (translationX != getTranslationX()) {
invalidateViewProperty(true, false);
// Sets the translation value for the display list on the X axis.
// 在display list 上設置了 translationX
mRenderNode.setTranslationX(translationX);
// 這里的invalidateViewProperty會調用invalidate方法,也就是會導致 view 重繪,但是,并不會導致 view 的位置發生改變(沒有 onLayout 調用)
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
上面可以看出,并沒有改變 view 位置,而是在display list 上設置了 translationX
關于 display list,這個就有些復雜了,暫不分析
那么還有一個問題需要 解決,就是為什么能收到點擊事件,這就需要看父布局(ViewGroup)如何判斷一個觸控坐標是否在子 view 范圍內了,在 ViewGroup 的 dispatchTouchEvent 事件中,有如下源碼 :
if (!canViewReceivePointerEvents(child)// 判斷是否可見,是否有動畫(View 動畫,即 Animation)正在播放
|| !isTransformedTouchPointInView(x, y, child, null)// 判斷坐標是否在 child 內
) {
continue;
}
protected boolean isTransformedTouchPointInView(float x, float y, View child,
PointF outLocalPoint) {
final float[] point = getTempPoint();
point[0] = x;
point[1] = y;
transformPointToViewLocal(point, child);// 將觸摸事件的坐標點按 child 中的相反矩陣轉換
final boolean isInView = child.pointInView(point[0], point[1]);// 判斷轉換后的點是否在 view 的位置內
if (isInView && outLocalPoint != null) {
outLocalPoint.set(point[0], point[1]);
}
return isInView;
}
public void transformPointToViewLocal(float[] point, View child) {
point[0] += mScrollX - child.mLeft;
point[1] += mScrollY - child.mTop;
if (!child.hasIdentityMatrix()) {// 如果設置了 translationX,這里肯定為 true
child.getInverseMatrix().mapPoints(point);// 將觸控坐標點做相反矩陣計算,這里是核心
}
}
final boolean hasIdentityMatrix() {
return mRenderNode.hasIdentityMatrix();
}
上面可以看出,這里在判斷子view 是否可以收到觸控事件時,做了觸控矩陣轉換,如果轉換后的觸控位置在子view 范圍內,則認為可以收到觸控事件;
上面涉及到矩陣 Matrix 類,很多人可能不是很清楚這個,關于這些,可能需要讀者自行學習,打開 Matrix 源碼,我們可以看到這樣一些方法:
public void setRotate(float degrees)
public void setScale(float sx, float sy)
public void setTranslate(float dx, float dy)
其實,在安卓中,關于 View 方面,很多平移,旋轉,放大縮小,都是通過Matrix 做坐標變換來完成的,包括 Canvas,Paint等繪圖基礎.
浙公網安備 33010602011771號