Android进阶之路读书笔记-Scroller解析
(此文接续 view滑动的方法 一文)
Scroller的构造方法:
public Scroller(Context context) {
this(context, null);
}
/**
* Create a Scroller with the specified interpolator. If the interpolator is
* null, the default (viscous) interpolator will be used. "Flywheel" behavior will
* be in effect for apps targeting Honeycomb or newer.
*/
public Scroller(Context context, Interpolator interpolator) {
this(context, interpolator,
context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
}
/**
* Create a Scroller with the specified interpolator. If the interpolator is
* null, the default (viscous) interpolator will be used. Specify whether or
* not to support progressive "flywheel" behavior in flinging.
*/
public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
mFinished = true;
if (interpolator == null) {
mInterpolator = new ViscousFluidInterpolator();
} else {
mInterpolator = interpolator;
}
mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
mFlywheel = flywheel;
mPhysicalCoeff = computeDeceleration(0.84f); // look and feel tuning
}
通常情况下我们会使用第一个构造方法(其实最终都是调用的第三个构造方法),不传入插值器默认使用ViscousFluidInterpolator这个插值器.然后是startScroll()方法
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
mFinalX = startX + dx;
mFinalY = startY + dy;
mDeltaX = dx;
mDeltaY = dy;
mDurationReciprocal = 1.0f / (float) mDuration;
}
这里面主要是保存传进来的参数,没有开启滑动的方法.startX和startY表示滑动开始的起点,dx和dy表
示滑动的距离,duration则表示滑动持续的时间.使view滑动主要是我们调用了invalidate()方法
public void smoothTo(int x1,int y1) {
int scrollX = getScrollX();
int scrollY = getScrollY();
int disX = x1 - scrollX;
int disY = y1 - scrollY;
scroller.startScroll(scrollX,scrollY,disX,disY,2000);
invalidate();
}
invalidate()方法会是view重新绘制,而View的重绘会调用View的draw()方法,draw()方法又会调用View的computeScroll()方法,即会调用我们重写的computeScroll()方法
/**
* 此方法会在onDraw()方法中调用
*/
@Override
public void computeScroll() {
super.computeScroll();
if (scroller.computeScrollOffset()){
((View)getParent()).scrollTo(scroller.getCurrX(),scroller.getCurrY());
invalidate();
}
}
接下来我们看下computeScrollOffset()方法(此方法返回true表示滑动未结束,false表示滑动结束)
/**
* Call this when you want to know the new location. If it returns true,
* the animation is not yet finished.
*/
public boolean computeScrollOffset() {
if (mFinished) {
return false;
}
int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
mCurrX = mStartX + Math.round(x * mDeltaX);
mCurrY = mStartY + Math.round(x * mDeltaY);
break;
case FLING_MODE:
final float t = (float) timePassed / mDuration;
final int index = (int) (NB_SAMPLES * t);
float distanceCoef = 1.f;
float velocityCoef = 0.f;
if (index < NB_SAMPLES) {
final float t_inf = (float) index / NB_SAMPLES;
final float t_sup = (float) (index + 1) / NB_SAMPLES;
final float d_inf = SPLINE_POSITION[index];
final float d_sup = SPLINE_POSITION[index + 1];
velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
distanceCoef = d_inf + (t - t_inf) * velocityCoef;
}
mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
// Pin to mMinX <= mCurrX <= mMaxX
mCurrX = Math.min(mCurrX, mMaxX);
mCurrX = Math.max(mCurrX, mMinX);
mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
// Pin to mMinY <= mCurrY <= mMaxY
mCurrY = Math.min(mCurrY, mMaxY);
mCurrY = Math.max(mCurrY, mMinY);
if (mCurrX == mFinalX && mCurrY == mFinalY) {
mFinished = true;
}
break;
}
}
else {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
return true;
}
首先计算了timePassed,也就是动画的持续时长,如果timePassed< mDuration(也就是说当动画持续时长小于我们设置的时长的时候),回去执行switch语句.在startScroll()方法中的mMode值为SCROLL_MODE,所以执行分支语句 SCROLL_MODE,然后根据插值器 Interpolator 来计算出在该时间段内移动的距离,赋值给mCurrX和mCurrY,这样我们就能通过Scroller的getCurrX()和getCurrY()方法来获取当前的ScrollX和ScrollY了
总结一下Scroller的原理
Scroller并不能直接实现View的滑动,它需要配合View的computeScroll()方法。在computeScroll()中不断让View进行重绘,每次重绘都会计算滑动持续的时间,根据这个持续时间就能算出这次View滑动的位置,我们根据每次滑动的位置调用scrollTo()方法进行滑动,这样不断地重复上述过程就形成了弹性滑动
上一篇: go流程控制代码详解