scrollTo,scrollBy

版权声明:本文为博主原创文章,未经博主允许下请随便转载。 https://blog.csdn.net/god_wen/article/details/88841620
   public void scrollBy(int x, int y) {
        scrollTo(mScrollX + x, mScrollY + y);
    }

 public void scrollTo(int x, int y) {
        if (mScrollX != x || mScrollY != y) {
            int oldX = mScrollX;
            int oldY = mScrollY;
            mScrollX = x;
            mScrollY = y;
       /**
     * Used to indicate that the parent of this view should clear its caches. This functionality
     * is used to force the parent to rebuild its display list (when hardware-accelerated),
     * which is necessary when various parent-managed properties of the view change, such as
     * alpha, translationX/Y, scrollX/Y, scaleX/Y, and rotation/X/Y. This method only
     * clears the parent caches and does not causes an invalidate event.
     *
     * @hide
     */
     //父容器清楚它的缓存,这个方法强制父容器重建显示列表(使用硬件加速的情况下)。
     //该功能是必须的,让有View属性改变时
     //例如 alpha, translationX/Y, scrollX/Y, scaleX/Y, and rotation/X/Y.
     //该方法智慧清楚父类换曾,不会引起重绘操作
     
            invalidateParentCaches();
            //滚动时调用监听
            onScrollChanged(mScrollX, mScrollY, oldX, oldY);
            //核心代码1
            //
            //
            if (!awakenScrollBars()) {
            //核心代码2
            //
            //
                postInvalidateOnAnimation();
            }
        }
    }

从中我们可以发现scrollBy调用scrollTo,且scrollTo会记录原来的位置坐标,如果位置参数一样每次调用都会不执行操作,而scrollBy会累加位置坐标。

用法总结

scrollTo移动到某位置,scrollBy 移动多少位置。

PS:
这就和英语中To 和By的用法一样

reduce the price to 2 yuan 降到2元
reduce the price by 2 yuan 降低了2元

我们接着看源码核心代码1的源码

protected boolean awakenScrollBars() {
        return mScrollCache != null &&
                awakenScrollBars(mScrollCache.scrollBarDefaultDelayBeforeFade, true);
    }
 protected boolean awakenScrollBars(int startDelay, boolean invalidate) {
        final ScrollabilityCache scrollCache = mScrollCache;

        if (scrollCache == null || !scrollCache.fadeScrollBars) {
            return false;
        }

        if (scrollCache.scrollBar == null) {
            scrollCache.scrollBar = new ScrollBarDrawable();
            scrollCache.scrollBar.setState(getDrawableState());
            scrollCache.scrollBar.setCallback(this);
        }

        if (isHorizontalScrollBarEnabled() || isVerticalScrollBarEnabled()) {

            if (invalidate) {
                // Invalidate to show the scrollbars
                postInvalidateOnAnimation();
            }

            if (scrollCache.state == ScrollabilityCache.OFF) {
                // FIXME: this is copied from WindowManagerService.
                // We should get this value from the system when it
                // is possible to do so.
                final int KEY_REPEAT_FIRST_DELAY = 750;
                startDelay = Math.max(KEY_REPEAT_FIRST_DELAY, startDelay);
            }

            // Tell mScrollCache when we should start fading. This may
            // extend the fade start time if one was already scheduled
            long fadeStartTime = AnimationUtils.currentAnimationTimeMillis() + startDelay;
            scrollCache.fadeStartTime = fadeStartTime;
            scrollCache.state = ScrollabilityCache.ON;

            // Schedule our fader to run, unscheduling any old ones first
            if (mAttachInfo != null) {
                mAttachInfo.mHandler.removeCallbacks(scrollCache);
                mAttachInfo.mHandler.postAtTime(scrollCache, fadeStartTime);
            }

            return true;
        }

        return false;
    }

你会发现,如果需要绘制scrollBar会调用postInvalidateOnAnimation 函数返回true,之后跳过最外层的postInvalidateOnAnimation的调用,如果不需要绘制scrollBar直接返回false,反而调用最外层的postInvalidateOnAnimation。

所以最后都会调用postInvalidateOnAnimation函数

   public void postInvalidateOnAnimation() {
        // We try only with the AttachInfo because there's no point in invalidating
        // if we are not attached to our window
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            attachInfo.mViewRootImpl.dispatchInvalidateOnAnimation(this);
        }
    }

最后我们会看见调用mViewRootImpl的dispatchInvalidateOnAnimation函数,这个mViewRootImpl学过View绘制流程的人都知道,他是控制着根View,且把要移动的View当参数传递进去。现在来看ViewRootImpl类。

 public void dispatchInvalidateOnAnimation(View view) {
        mInvalidateOnAnimationRunnable.addView(view);
    }
    final InvalidateOnAnimationRunnable mInvalidateOnAnimationRunnable =
            new InvalidateOnAnimationRunnable();
    final class InvalidateOnAnimationRunnable implements Runnable {
        private boolean mPosted;
        //mViews 是一个数组,存储着需要滚动的View
        private final ArrayList<View> mViews = new ArrayList<View>();
        private final ArrayList<AttachInfo.InvalidateInfo> mViewRects =
                new ArrayList<AttachInfo.InvalidateInfo>();
        private View[] mTempViews;
        private AttachInfo.InvalidateInfo[] mTempViewRects;

        public void addView(View view) {
            synchronized (this) {
                mViews.add(view);
          
                postIfNeededLocked();
            }
        }

     //。。。。
        @Override
        public void run() {
            final int viewCount;
            final int viewRectCount;
            synchronized (this) {
                mPosted = false;

                viewCount = mViews.size();
                if (viewCount != 0) {
                    mTempViews = mViews.toArray(mTempViews != null
                            ? mTempViews : new View[viewCount]);
                    mViews.clear();
                }

                viewRectCount = mViewRects.size();
                if (viewRectCount != 0) {
                    mTempViewRects = mViewRects.toArray(mTempViewRects != null
                            ? mTempViewRects : new AttachInfo.InvalidateInfo[viewRectCount]);
                    mViewRects.clear();
                }
            }
//核心代码
//
//
            for (int i = 0; i < viewCount; i++) {
                mTempViews[i].invalidate();
                mTempViews[i] = null;
            }

            for (int i = 0; i < viewRectCount; i++) {
                final View.AttachInfo.InvalidateInfo info = mTempViewRects[i];
                info.target.invalidate(info.left, info.top, info.right, info.bottom);
                info.recycle();
            }
        }

        private void postIfNeededLocked() {
         //mPosted初始值是false,这里保证该方法只被执行一次.再run方法执行之后会被重新赋值成false.
            if (!mPosted) {
            //看多我之前发分析invalidate我们可以知道,这个方法其实
                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
                mPosted = true;
            }
        }
    }

mViews时一个数组,存储着需要被滚动的View,拖过异步消息,最后UI线程会进行跟新,通过invalidate方法。
这个方法最终会进行组建的测量,布局,绘制过程。

看过我之前写的[android 自定义控件](4)View的绘制流程可以知道,绘制请求最终调用下面版本的invalidate

public void invalidate(int l, int t, int r, int b) {
        final int scrollX = mScrollX;
        final int scrollY = mScrollY;
        invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true, false);
    }

发现都是减我们scoll变量,所以写负的scoll变量View就像做移动,写正德scoll变量就像右移动。

猜你喜欢

转载自blog.csdn.net/god_wen/article/details/88841620
今日推荐