scrollBy()源码:
/**
* Move the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the amount of pixels to scroll by horizontally
* @param y the amount of pixels to scroll by vertically
*/
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
scrollTo()源码:
/**
* Set the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the x position to scroll to
* @param y the y position to scroll to
*/
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
好的,下面进行源码解析:
首先,简单介绍:
scrollBy---在现有的基础上继续移动视图的内容
scrollTo---将一个视图的内容移动到指定位置
然后,深入分析:
scrollBy方法,实际上还是调用了scrollTo()方法。将mScrollX加上scrollBy的第一个传
参x,将mScrollY加上scrollBy的第二个传参,然后调用scrollTo。
mScrollX和mScrollY是View中定义的两个变量,分别表示View的内容相对于View本身在
水平和垂直方向上的偏移量。
scrollTo方法,首先我们看这行代码
if(mScrollX != x || mScrollY != y)
我们可以看到,如果满足这个判断,scrollTo方法是没有做任何事情的。为什么要有
这个判断呢?原因是:如果这次移动的点的坐标和上次的一样,那么就没有必要进行移动了。
接着看,
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
这是用两个局部变量将之前的mScrollX和mScrollY保存下来,再将新的赋值给mScrollX和mScrollY,再来看,
invalidateParentCaches();
这是用于指示此视图的父级应清除其缓存,
再来看,
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
调用了onScrollChanged方法,在方法里面回调了onScrollChange,源码:
mListenerInfo.mOnScrollChangeListener.onScrollChange(this, l, t, oldl, oldt);
最后执行了,
postInvalidateOnAnimation();
这个方法会通知View进行重绘,所以进入到draw()
我们知道draw()中有一步就是:绘制装饰,例如滚动条。
scrollbars就是由于scroll引起的,所以再来看这一步的具体实现:
public void onDrawForeground(Canvas canvas) {
onDrawScrollIndicators(canvas);
onDrawScrollBars(canvas);
...
}
onDrawScrollBars(canvas)分别绘制了水平和垂直方向的scrollBar,最终都会调用
invalidate方法。
该方法源码如下:
public void invalidate(Rect dirty) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
invalidateInternal(dirty.left - scrollX, dirty.top - scrollY,
dirty.right - scrollX, dirty.bottom - scrollY, true, false);
}
我们再跟进去看,
void invalidateInternal(int l, int t, int r, int b,
boolean invalidateCache, boolean fullInvalidate) {
...
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
p.invalidateChild(this, damage);
...
}
我们发现在这里重绘所有子控件,重绘时Rect的left、top减去了mScrollX,right、bottom减去了mScrollY。
因此我们也可以明白为什么当scrollX为正时,往左移动。