Android View Scroller问答

android中的scroll一般是调用View.scrollTo()函数实现的,另外有一个View.scrollBy()其实现也是View.scrollTo()。关于View.scrollTo(),下面有一些问题来探究一下,本文所查看的代码是android4.2.2的源代码。

  • 问:View.scrollTo()的原理是什么?
    答:在android.view.View.java中有两个变量mScrollX和mScrollY,两个变量的定义如下:

    /**
     * The offset, in pixels, by which the content of this view is scrolled
     * horizontally.
     */
    protected int mScrollX;
    
    /**
     * The offset, in pixels, by which the content of this view is scrolled
     * vertically.
     */
    protected int mScrollY;
    


    在View的绘制过程中,会根据这两个值对里面的内容进行偏移。View.draw()中的代码片段如下:

    int left = mScrollX + paddingLeft;
    int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
    int top = mScrollY + getFadeTop(offsetRequired);
    int bottom = top + getFadeHeight(offsetRequired);
    


    在View.scrollTo()的作用就是就是改变这两个变量,从而引起View的content位置发生变化。

  • 问:Scroll是否引起Layout的改变?
    答:Scroll只是改变View.mScrollx和View.mScrollY,并且在draw()函数中,根据这两个偏移量进行绘制。所以只是影响draw(),不会引起Layout的改变。
  • 问:如果scroll只是在绘制的时候进行偏移,那么对touch事件的派发是否产生影响,View事件的响应是在scroll前的位置还是scroll后的位置?
    答:事件的分派是在ViewGroup.dispatchTouchEvent()中,查看源码,发现在寻找响应事件的子View时,会算上mScrollX和mScrollY的偏移。所以scroll是会对事件派发产生影响的。View事件的响应是在scroll后的位置。关键代码如下:

    protected boolean isTransformedTouchPointInView(float x, float y, View child,
            PointF outLocalPoint) {
        float localX = x + mScrollX - child.mLeft;
        float localY = y + mScrollY - child.mTop;
        if (! child.hasIdentityMatrix() && mAttachInfo != null) {
            final float[] localXY = mAttachInfo.mTmpTransformLocation;
            localXY[0] = localX;
            localXY[1] = localY;
            child.getInverseMatrix().mapPoints(localXY);
            localX = localXY[0];
            localY = localXY[1];
        }
        final boolean isInView = child.pointInView(localX, localY);
        if (isInView && outLocalPoint != null) {
            outLocalPoint.set(localX, localY);
        }
        return isInView;
    }
    

  • 问:Scroller的作用是什么?
    答:Scroller的作用是帮助Scroll更加顺滑的。如果我们不是想在一段时间内,有A点顺滑的scroll到B点。这样我们就要计算每次draw应该偏移多少,才能达到顺滑的效果。Scroller就是用来帮助计算的。具体的计算方式是在Scroller.computeScrollOffset()函数中。代码如下:

    public boolean computeScrollOffset() {
    if (mFinished) {
        return false;
    }
    
    int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
    
    if (timePassed < mDuration) {
        switch (mMode) {
        case SCROLL_MODE:
            float x = timePassed * mDurationReciprocal;
    
            if (mInterpolator == null)
                x = viscousFluid(x); 
            else
                x = mInterpolator.getInterpolation(x);
    
            mCurrX = mStartX + Math.round(x * mDeltaX);
            mCurrY = mStartY + Math.round(x * mDeltaY);
            break;
        case FLING_MODE:
            final float t = (float) timePassed / mDuration;
            final int index = (int) (NB_SAMPLES * t);
            float distanceCoef = 1.f;
            float velocityCoef = 0.f;
            if (index < NB_SAMPLES) {
                final float t_inf = (float) index / NB_SAMPLES;
                final float t_sup = (float) (index + 1) / NB_SAMPLES;
                final float d_inf = SPLINE_POSITION[index];
                final float d_sup = SPLINE_POSITION[index + 1];
                velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
                distanceCoef = d_inf + (t - t_inf) * velocityCoef;
            }
    
            mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
    
            mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
            // Pin to mMinX <= mCurrX <= mMaxX
            mCurrX = Math.min(mCurrX, mMaxX);
            mCurrX = Math.max(mCurrX, mMinX);
    
            mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
            // Pin to mMinY <= mCurrY <= mMaxY
            mCurrY = Math.min(mCurrY, mMaxY);
            mCurrY = Math.max(mCurrY, mMinY);
    
            if (mCurrX == mFinalX && mCurrY == mFinalY) {
                mFinished = true;
            }
    
            break;
        }
    }
        else {
            mCurrX = mFinalX;
            mCurrY = mFinalY;
            mFinished = true;
        }
        return true;
    }
    

  • 问:Scroll和ValueAnimator的区别是什么?
    答:ValueAnimator的本质是在一段时间内,按照一定的规律去改变一个值。而scroll就是改变mScrollX和mScrollY的值。所以理论上也可以用ValueAnimator来改变mScrollX和mScrollY,从而达到scroll的目的。
  • 问:Scroll和Animation的区别是什么?
    答:animation也是通过在draw函数里面实现的,但是animation只是改变了绘制的位置,并没有影响到事件的响应。所以当一个View从A点通过Animation动画移动到B点,View的事件继续在A点才能响应。
上一篇:虚拟机上keepalived实验笔记


下一篇:Item 9 覆盖equals时总要覆盖hashCode