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。