scrollTo,scrollBy r囧r小猫 2022-02-27 09:38 171阅读 0赞 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的绘制流程][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变量就像右移动。 [android _4_View]: https://blog.csdn.net/god_wen/article/details/70623375
还没有评论,来说两句吧...