首先,需要知道的是,View是可以延伸到屏幕之外的,可以想象一下ListVIew或GridView。也就是说View的尺寸可以超过屏幕的尺寸。View的大小就是onDraw()中Canvas画布的大小。Canvas可以做translate()、clipRec()t等变换,可以说Canvas是无边界的。而我们在屏幕上所见到的,只是Canvas的一部分而已。可以调用View的scrollTo()和scrollBy()将视图绘制到指定区域。那么View中的scrollTo()和scrollBy()又是怎么回事呢?
要想一探究竟,就需要研究一下View的源码。
在View的源码中,mScrollX和mScrollY是视图在X轴和Y轴的偏移量。源码注释说的非常清楚mScrollX和mScrollY代表的是什么。
-
-
-
-
-
- @ViewDebug.ExportedProperty(category = "scrolling")
- protected int mScrollX;
-
-
-
-
-
- @ViewDebug.ExportedProperty(category = "scrolling")
- protected int mScrollY;
-
-
-
-
-
-
-
-
-
-
- public final int getScrollX() {
- return mScrollX;
- }
-
-
-
-
-
-
-
-
- public final int getScrollY() {
- return mScrollY;
- }
知道了mScrollX和mScrollY的含义,接下来再看scrollTo()和scrollBy()的具体实现,代码如下:
-
-
-
-
-
-
-
- public void scrollTo(int x, int y) {
- if (mScrollX != x || mScrollY != y) {
- int oldX = mScrollX;
- int oldY = mScrollY;
- mScrollX = x;
- mScrollY = y;
- onScrollChanged(mScrollX, mScrollY, oldX, oldY);
- if (!awakenScrollBars()) {
- invalidate();
- }
- }
- }
-
-
-
-
-
-
-
-
- public void scrollBy(int x, int y) {
- scrollTo(mScrollX + x, mScrollY + y);
- }
从源码中可以看到,scrollBy()的内部其实是调用了scrollTo()。在scrollTo()中,调用了onScrollChanged()和invalidate()。
onScrollChanged()的作用就是告诉系统(可以理解为Android框架),这个View的scrollTo()或scrollBy()曾经被调用过;而invalidate()是告诉系统,这个View需要被重新绘制。
接下来,探究一下onScrollChanged()和invalidate()的具体实现,代码如下:
-
-
-
-
-
-
-
-
-
-
-
- protected void onScrollChanged(int l, int t, int oldl, int oldt) {
- mBackgroundSizeChanged = true;
-
- final AttachInfo ai = mAttachInfo;
- if (ai != null) {
- ai.mViewScrollChanged = true;
- }
- }
-
-
-
-
-
- public void invalidate() {
- if (ViewDebug.TRACE_HIERARCHY) {
- ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
- }
-
- if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)) {
- mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
- final ViewParent p = mParent;
- final AttachInfo ai = mAttachInfo;
- if (p != null && ai != null) {
- final Rect r = ai.mTmpInvalRect;
- r.set(0, 0, mRight - mLeft, mBottom - mTop);
-
-
- p.invalidateChild(this, r);
- }
- }
- }
知道了scrollTo()和scrollBy()的意义,那么举个例子,感性地认识一下。
假设有一个View,它叫做SView。
如果想把SView从(0, 0)移动到(100, 100)。注意,这里说的(0, 0)和(100, 100),指的是SView左上角的坐标。那么偏移量就是原点(0, 0)到目标点(100, 100)的距离,即(0 , 0) - (100, 100) = (-100, -100)。
只需要调用SView.scrollTo(-100, -100)就可以了。请再次注意,scrollTo(int x, int y)的两个参数x和y,代表的是偏移量,这时的参照物是(0, 0)点。
然而,scrollBy()是有一定的区别的。scrollBy()的参照物是(0, 0)点加上偏移量之后的坐标。
这么描述比较抽象,举个例子。假设SView调用了scrollTo(-100, -100),此时SView左上角的坐标是(100, 100),这时再调用scrollBy(-20, -20),此时SView的左上角就被绘制到了(120, 120)这个位置。
总结一下,scrollTo()是一步到位,而scrollBy()是逐步累加。
那么mScrollX和mScrollY又是在哪里被使用的呢?
上面说过,scrollTo()会使视图重绘,那究竟是如何绘制的?请看draw()方法,代码如下:
-
-
-
-
-
-
-
-
- public void draw(Canvas canvas) {
- if (ViewDebug.TRACE_HIERARCHY) {
- ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
- }
-
- final int privateFlags = mPrivateFlags;
- final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
- (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
- mPrivateFlags = (privateFlags & ~DIRTY_MASK) | DRAWN;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- int saveCount;
-
- if (!dirtyOpaque) {
- final Drawable background = mBGDrawable;
- if (background != null) {
- final int scrollX = mScrollX;
- final int scrollY = mScrollY;
-
- if (mBackgroundSizeChanged) {
- background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
- mBackgroundSizeChanged = false;
- }
-
- if ((scrollX | scrollY) == 0) {
- background.draw(canvas);
- } else {
- canvas.translate(scrollX, scrollY);
- background.draw(canvas);
- canvas.translate(-scrollX, -scrollY);
- }
- }
- }
-
-
- final int viewFlags = mViewFlags;
- boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
- boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
- if (!verticalEdges && !horizontalEdges) {
-
- if (!dirtyOpaque) onDraw(canvas);
-
-
- dispatchDraw(canvas);
-
-
- onDrawScrollBars(canvas);
-
-
- return;
- }
-
-
-
-
-
-
-
-
- boolean drawTop = false;
- boolean drawBottom = false;
- boolean drawLeft = false;
- boolean drawRight = false;
-
- float topFadeStrength = 0.0f;
- float bottomFadeStrength = 0.0f;
- float leftFadeStrength = 0.0f;
- float rightFadeStrength = 0.0f;
-
-
- int paddingLeft = mPaddingLeft;
- int paddingTop = mPaddingTop;
-
- final boolean offsetRequired = isPaddingOffsetRequired();
- if (offsetRequired) {
- paddingLeft += getLeftPaddingOffset();
- paddingTop += getTopPaddingOffset();
- }
-
- int left = mScrollX + paddingLeft;
- int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
- int top = mScrollY + paddingTop;
- int bottom = top + mBottom - mTop - mPaddingBottom - paddingTop;
-
- if (offsetRequired) {
- right += getRightPaddingOffset();
- bottom += getBottomPaddingOffset();
- }
-
- final ScrollabilityCache scrollabilityCache = mScrollCache;
- int length = scrollabilityCache.fadingEdgeLength;
-
-
-
- if (verticalEdges && (top + length > bottom - length)) {
- length = (bottom - top) / 2;
- }
-
-
- if (horizontalEdges && (left + length > right - length)) {
- length = (right - left) / 2;
- }
-
- if (verticalEdges) {
- topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
- drawTop = topFadeStrength >= 0.0f;
- bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
- drawBottom = bottomFadeStrength >= 0.0f;
- }
-
- if (horizontalEdges) {
- leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
- drawLeft = leftFadeStrength >= 0.0f;
- rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
- drawRight = rightFadeStrength >= 0.0f;
- }
-
- saveCount = canvas.getSaveCount();
-
- int solidColor = getSolidColor();
- if (solidColor == 0) {
- final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
-
- if (drawTop) {
- canvas.saveLayer(left, top, right, top + length, null, flags);
- }
-
- if (drawBottom) {
- canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
- }
-
- if (drawLeft) {
- canvas.saveLayer(left, top, left + length, bottom, null, flags);
- }
-
- if (drawRight) {
- canvas.saveLayer(right - length, top, right, bottom, null, flags);
- }
- } else {
- scrollabilityCache.setFadeColor(solidColor);
- }
-
-
- if (!dirtyOpaque) onDraw(canvas);
-
-
- dispatchDraw(canvas);
-
-
- final Paint p = scrollabilityCache.paint;
- final Matrix matrix = scrollabilityCache.matrix;
- final Shader fade = scrollabilityCache.shader;
- final float fadeHeight = scrollabilityCache.fadingEdgeLength;
-
- if (drawTop) {
- matrix.setScale(1, fadeHeight * topFadeStrength);
- matrix.postTranslate(left, top);
- fade.setLocalMatrix(matrix);
- canvas.drawRect(left, top, right, top + length, p);
- }
-
- if (drawBottom) {
- matrix.setScale(1, fadeHeight * bottomFadeStrength);
- matrix.postRotate(180);
- matrix.postTranslate(left, bottom);
- fade.setLocalMatrix(matrix);
- canvas.drawRect(left, bottom - length, right, bottom, p);
- }
-
- if (drawLeft) {
- matrix.setScale(1, fadeHeight * leftFadeStrength);
- matrix.postRotate(-90);
- matrix.postTranslate(left, top);
- fade.setLocalMatrix(matrix);
- canvas.drawRect(left, top, left + length, bottom, p);
- }
-
- if (drawRight) {
- matrix.setScale(1, fadeHeight * rightFadeStrength);
- matrix.postRotate(90);
- matrix.postTranslate(right, top);
- fade.setLocalMatrix(matrix);
- canvas.drawRect(right - length, top, right, bottom, p);
- }
-
- canvas.restoreToCount(saveCount);
-
-
- onDrawScrollBars(canvas);
- }
参考资料
http://blog.csdn.net/qinjuning/article/details/7247126
http://blog.csdn.net/vipzjyno1/article/details/24577023
http://blog.csdn.net/xiaoguochang/article/details/8655210
http://developer.android.com/reference/android/view/View.html#scrollTo%28int,%20int%29
转自:http://blog.csdn.net/manoel/article/details/39228593
0 0