谷歌为什么要设计一个scroller?
在Android中所有的的View都有一个实际界面大于可视界面的,这就涉及到界面的移动或者说偏移,View这个类提供了scrollTo和ScrollBy方法来实现界面的滚动,但是这两种滚动都是即刻瞬间的,对于用户来说是不友好的,这个时候就需要一个滚动器来拉长这个滚动过程。也就是我们的Scroller,这个滚动器的构造方法需要一个durration来设置滚动时间。
Scroller的作用的是什么?
实际上Scroller并不负责界面的实际滚动,虽然这个名字让人误解,但实际上它就是一个工具类,它有两个最重要的方法,一个是startScroll(),这个方法就是根据scroller的构造方法中的x,y,distance,durration这些个参数来初始化一些参数,看源码,和我的注释,不必多言:
方法一:
/**
* Start scrolling by providing a starting point and thedistance to travel.
*
* @param startX Starting horizontal scroll offset inpixels. Positive
* numbers will scroll thecontent to the left.
* @param startY Starting vertical scroll offset in pixels.Positive numbers
* will scroll the content up.
* @param dx Horizontal distance to travel. Positive numberswill scroll the
* content to the left.
* @param dy Vertical distance to travel. Positive numberswill scroll the
* content up.
* @param duration Duration of the scroll in milliseconds.
*/
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;
//重点横坐标=起点坐标与distance的和
mFinalX = startX + dx;
mFinalY = startY + dy;
mDeltaX = dx;
mDeltaY = dy;
//持续时间的一个倒数,后面更新滚动后的坐标
mDurationReciprocal = 1.0f / (float) mDuration;
}
方法二:
/**
* Call this when you want to know the new location. If it returns true,
* the animation is not yet finished. loc will bealtered to provide the
* new location.
*/
public boolean computeScrollOffset() {
if (mFinished) {
return false;
}
int timePassed =(int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
float x = timePassed *mDurationReciprocal;
if (mInterpolator ==null)
x =viscousFluid(x);
else
x =mInterpolator.getInterpolation(x);
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);
final float t_inf =(float) index / NB_SAMPLES;
final float t_sup =(float) (index + 1) / NB_SAMPLES;
final float d_inf =SPLINE[index];
final float d_sup = SPLINE[index+ 1];
final floatdistanceCoef = d_inf + (t - t_inf) / (t_sup - t_inf) * (d_sup - d_inf);
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;
}
这个方法无需多言,也是更新mCurrY 等状态值。
综上所述,scroller这个类就是一个工具类,get,set一些方法罢了,并不能帮助我们滚动。
如何实现滚动?
android View类的scrollTo()方法结合Scroller一起使用。具体就是View初始化的时候startScroll,然后去实现每一个ViewGroup中都要实现的一个computeScroll方法,这个方法才是真正控制滚动的方法。我们只需要在我们的ViewGroup中调用这两个方法即可。具体就是copumteScroll中
if (mScroller.computeScrollOffset()) {//为true的时候说明没有滚动到终点
if (getScrollX() !=mScroller.getCurrX()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
}
invalidate();//scrollTo会导致界面刷新,invalidate()会再一次刷新,这样会形成一个不间断刷新过程
}
View调用computeScroll的原理及过程?
View draw(Canvas can)有6个过程:
1.绘制bg
2.保存一些Canvas的layer的状态,为横竖的fadding edge绘制做准备
3.绘制content,也就是调用OnDraw方法来调用View具体的绘图实现。
4.绘制childView,这个是针对ViewGroup的,在View中这个是一个空方法,在ViewGroup中的这个方法中会调用drawChild这个方法,这个方法就会调用computeScroll方法,实现滚动。
5..恢复canvas 的layer状态,绘制faddingedge,
6.绘制scrollBar)