getScrollY()是自定义View中的常客,但是由于android特有的坐标系以及屏幕与View之间的复杂关系,往往是分析和看源码的难点,这里结合示意图帮助理解getScrollY,一劳永逸。
首先我们需要了解清楚几个概念,就是我们经常看到的向上/下滚动以及上/下滑这四个名词:
!!!记住滚动和滑动在android中的含义是不同的!!!
上滑/下滑是我们手指相对于整个屏幕而言的,手指从屏幕上方滑动到下方,称为下滑,反之称为上滑。
向上/下滚动一般是View相对于屏幕而言的,我们假定下面左图中的场景:
当我们说向上滚动时,意味着我们可以看到View的上半部分,所以相对于屏幕来说View整体下移,也即手指触摸点下移,可以看作是手指拉着View的内容向下移动,也即下滑,如上右图。
所以所说的向上滚动对应着下滑,而向下滚动对应着上滑。
首先需要知道,
scrollTo
这个方法移动的是View
的内容,而其位置是不变的,就像属性动画和补间动画的区别一样。
标题这句话与Android的坐标系正好相反,这就是scrollTo和getScrollY让人难以理解的地方。我们不妨先来看看这两个方法的源码实现:
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();
}
}
}
public final int getScrollY() {
return mScrollY;
}
可以看出getScrollY
方法返回的正是在scrollTo
中设置的y
,也就是当前View的内容
相对于当前View
的y方向偏移量。在测试中我们可以看到:
当y>0时,也就是getScrollY>0,这时候View的内容相对于其View容器位于左侧;
当y<0时,也就是getScrollY<0,这时候View的内容相对于其View容器位于右侧;
参见下图:
根据以上特点,我们可以将getScrollY的返回值简单的记忆成:View容器
左上顶点的y值减去View的内容
左上顶点的y值。
如果还是不太理解可以看下图:
我们可以将屏幕看作静止不动的,而View的内容
是在一条长长的画布上,其View容器假定为屏幕位置且固定不动,初始状态时View内容位于View的最左边,也就是getScrollX返回0
,当我们调用scrollTo(-100, 0)
时,也就是getScrollX返回-100
,按照我们上面说的,也就是
y(容器) - y(内容)= -100;
所以内容的左上角比容器的左上角,其横坐标要向右偏移100个单位,也就是上面下方的图。
其他情况同理,这就是表面上scrollTo
和getScrollY
的用法,其实记住这些也就够用了,但是为了更深入理解还是源码最有用。
这里有一步步带你深入源码解析scrollTo。
最重要的部分如下:
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);
}
这个invalidateInternal
方法是被View
的invalidate
方法调用的,用来绘制新内容的位置,而这个dirty
显然是旧的内容所表示的Rect
,可以看出,在参数中设置的都是减去scrollX,scrollY,将上面的例子带入就能看出新内容是往反方向移动了。