Android View.scrollTo()和scrollBy()详解
一直以来View.scrollTo和View.scrollBy一直是困扰Android开发者的一个难题,网络上对于这个问题的解释也很多,但一般都是抄来抄去的,且大多文章都没有讲清楚
(其实有那么一些时刻是讲清楚了,但是又被他们绕进去了)
首先直接看着两个方法的实现:
/**
* 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);
}
/**
* 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来实现的,scrollTo的功能是将View的内容滚动到相对于View本身的特定位置.
而通过scrollBy的具体实现可以推断出,scrollBy的功能是将View的内容相对于View本身,在当前情况下,再在水平和竖直方向滚动x和y距离
对于上述代码,其中很重要的是mScrollX和mScrollY两个参数.
其意义在源码中表示如下:
/**
* The offset, in pixels, by which the content of this view is scrolled
* horizontally.
* {@hide}
*/
protected int mScrollX;
/**
* The offset, in pixels, by which the content of this view is scrolled
* vertically.
* {@hide}
*/
protected int mScrollY;
/**
* Set the horizontal scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param value the x position to scroll to
*/
public void setScrollX(int value) {
scrollTo(value, mScrollY);
}
/**
* Set the vertical scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param value the y position to scroll to
*/
public void setScrollY(int value) {
scrollTo(mScrollX, value);
}
/**
* Return the scrolled left position of this view. This is the left edge of
* the displayed part of your view. You do not need to draw any pixels
* farther left, since those are outside of the frame of your view on
* screen.
*
* @return The left edge of the displayed part of your view, in pixels.
*/
public final int getScrollX() {
return mScrollX;
}
/**
* Return the scrolled top position of this view. This is the top edge of
* the displayed part of your view. You do not need to draw any pixels above
* it, since those are outside of the frame of your view on screen.
*
* @return The top edge of the displayed part of your view, in pixels.
*/
public final int getScrollY() {
return mScrollY;
}
mScrollX:表示view的内容离view起始位置的x水平方向的滚动偏移量
mScrollY:表示view的内容离view起始位置的y垂直方向的滚动偏移量
上述代码中可以看出,setScrollX/Y是通过scrollTo的方式来实现的,而在View的源代码中,只在scrollTo中对mScrollX/Y进行了赋值.
对于mScrollX/Y的值的具体解释如下:
1. View.mScrollX的值总是等于View的左边缘和View内容的左边缘在水平方向上的距离.View左边缘在view内容左边缘的右边时,mScrollX为正值,反之为负值.
2. View.mScrollY的值总是等于view的上边缘和view内容的上边缘在竖直方向上的距离.View上边缘在view内容上边缘的下边时,mScrollY为正值,反之为负值.
上面这一段话摘自《Android开发艺术探索》,经过验证也的确是如此(验证代码见文章末尾),但是比较难于理解和记忆(念起来都比较费劲),
而且,如果细心观察的话,可以发现:
既然说了是移动view的内容,那么view内容向右/下移动时,也就是向屏幕坐标系方向移动时,mScrollX应该为正值才对(这里假设view没有移动过,也就是mScrollX/Y=0),而上述结果完全与屏幕坐标系相反,这是为什么呢?
可惜的是,其原因并没有在源码中有很明显的体现(在View的源代码代码中,只在scrollTo中对mScrollX/Y进行了赋值,这个过程中会将新传入的x,y值赋给mScrollX/Y),
但是实际效果的确是这样.我也在网上找了一些资料,发现大多要么直接
选择忽略这个问题,将这当做理所当然,要么就说什么画布无限大之类的,在我看来,都无法解释为什么这里的正负符号与屏幕坐标系完全相反.
最终找到如下说法(出处已经找不到了…):
scroll本身滚动的就是内容,view本身的位置没有改变。至于正负关系的问题,可以这样解释:在移动平台中,要明确知道“滑动”与“滚动”的不同,具体来说,滑动和滚动的方向总是相反的.
滑动:英文翻译为translate,在Android的动画中有移动的动画(但是其中的参数直接使用的from->to的方式),表示将对象移动至目的地
滚动:英文翻译为scroll,其实我们通常看滚动条就可以看出来,当滚动条向下移动时,屏幕原本显示的内容向上移动;
当滚动条向左移动时,屏幕原本显示
的内容向右移动。这也就可以解释为什么scrollBy(int x, int y)中为什么x>0时,view的内容向左移动;y>0时,view的向上移动了。
上述说法是不是真的正确,Android本身是不是这么实现的我不得而知,但是其对于”为什么这里的正负符号与屏幕坐标系完全相反”这个问题的解释在结果完全相符合验证结果,
so…..
fact matters,coincidence doesn’t
如果它是事实,则巧合无关大雅
验证代码下载地址引自android 布局之滑动探究 scrollTo 和 scrollBy 方法使用说明
写完发现还有一篇博客降了scrollTo中滚动方向问题的博客:Android 浅谈scrollTo和scrollBy源码
上一篇: 上拉自动加载的recyclerView
下一篇: RecyclerView的基本用用法
推荐阅读
-
Android实现状态栏和虚拟按键背景颜色的变化实例代码详解
-
Android搜索框(SearchView)的功能和用法详解
-
Android搜索框SearchView属性和用法详解
-
Android搜索框(SearchView)的功能和用法详解
-
Android TraceView和Lint使用详解及性能优化
-
Android MotionEvent中getX()和getRawX()的区别实例详解
-
Android中dip、dp、sp、pt和px的区别详解
-
Android下载进度监听和通知的处理详解
-
Android 动态显示和隐藏状态栏详解及实例
-
Android RecyclerView添加头部和底部实例详解