ScaleGestureDetector縮放坑點
我們知道ScaleGestureDetector可使用getScaleFactor()來獲取縮放因子,但是我們在使用SimpleOnScaleGestureListener的時候會有一些坑點,我們來看下具體的源碼。
public float getScaleFactor() {
if (inAnchoredScaleMode()) {
// Drag is moving up; the further away from the gesture
// start, the smaller the span should be, the closer,
// the larger the span, and therefore the larger the scale
final boolean scaleUp =
(mEventBeforeOrAboveStartingGestureEvent && (mCurrSpan < mPrevSpan)) ||
(!mEventBeforeOrAboveStartingGestureEvent && (mCurrSpan > mPrevSpan));
final float spanDiff = (Math.abs(1 - (mCurrSpan / mPrevSpan)) * SCALE_FACTOR);
return mPrevSpan <= 0 ? 1 : scaleUp ? (1 + spanDiff) : (1 - spanDiff);
}
return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1;
}
分析源碼可知縮放因子與 mCurrSpan 和 mPrevSpan 有着緊密的聯繫,大部分情況下爲 mCurrSpan / mPrevSpan,mCurrSpan 爲當前兩指觸摸點的距離,
mPrevSpan 爲上一次兩指觸摸點的距離,我們可以通過 getCurrentSpan() 和 getPreviousSpan() 獲取到這兩個參數的值,然後我們來測試這兩個值的變化情況。
可以看到,當我的兩指間距在放大和縮小後,mCurrSpan 會隨着手指間距的變化而變化,但是 mPrevSpan 卻沒有任何變化,所以可能導致我們手指間距變小時,mCurrSpan 依然比 mPrevSpan 大,從而導致內容還是在繼續放大而不是我們希望的縮小。
我們來看下 mPrevSpan 更新的源碼。
final int minSpan = inAnchoredScaleMode() ? mSpanSlop : mMinSpan;
if (!mInProgress && span >= minSpan &&
(wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) {
mPrevSpanX = mCurrSpanX = spanX;
mPrevSpanY = mCurrSpanY = spanY;
mPrevSpan = mCurrSpan = span;
mPrevTime = mCurrTime;
//這裏onScaleBegin()返回值如果爲false,則mPrevSpan一定會更新
mInProgress = mListener.onScaleBegin(this);
}
// Handle motion; focal point and span/scale factor are changing.
if (action == MotionEvent.ACTION_MOVE) {
mCurrSpanX = spanX;
mCurrSpanY = spanY;
mCurrSpan = span;
boolean updatePrev = true;
if (mInProgress) {
//如果mInProgress爲true, onScale()返回false, 則mPrevSpan不會更新
updatePrev = mListener.onScale(this);
}
if (updatePrev) {
mPrevSpanX = mCurrSpanX;
mPrevSpanY = mCurrSpanY;
mPrevSpan = mCurrSpan;
mPrevTime = mCurrTime;
}
}
查看了源碼後我們發現問題,mPrevSpan的更新與否和我們之前綁定的OnScaleGestureListener有直接的關係,也就是我們要確保onScaleBegin()返回true的時候,onScale()也要返回true。
我們再來看一下官方默認實現OnScaleGestureListener接口的SimpleOnScaleGestureListener的源碼。
public static class SimpleOnScaleGestureListener implements OnScaleGestureListener {
public boolean onScale(ScaleGestureDetector detector) {
return false;
}
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
public void onScaleEnd(ScaleGestureDetector detector) {
// Intentionally empty
}
}
從源碼我們可以知道如果我們使用了SimpleOnScaleGestureListener,那麼mPrevSpan就永不更新,縮放因子就會出現異常。所以我們在使用ScaleGestureDetector的時候如果需要 mPrevSpan 及時更新的話就需要去實現OnScaleGestureListener 不能使用SimpleOnScaleGestureListener。