android Scroller原理分析

本文深入探讨了Android中Scroller类如何实现view滑动效果,包括其工作流程、关键成员变量及使用方法,详细解释了startScroll()和fling()方法的作用,并展示了在实际开发中如何应用Scroller来实现流畅的滚动动画。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在android中,Scroller是用来实现view的滑动效果。使用的步骤如下:
1. 创建Scroller对象
2. 调用Scroller.startScoller()或者fling()方法。
3. 在调用startScroller()或者fling()方法后调用invalidate(),促使view重绘
4. 重写view的computeScroll()方法。
其中View的computeScroll()是在view树draw()的时候调用。

Scroller类中主要的成员变量有:

private int mMode;
65 
66     private int mStartX;
67     private int mStartY;
68     private int mFinalX;
69     private int mFinalY;
70 
71     private int mMinX;
72     private int mMaxX;
73     private int mMinY;
74     private int mMaxY;
75 
76     private int mCurrX;
77     private int mCurrY;
78     private long mStartTime;
79     private int mDuration;
80     private float mDurationReciprocal;
81     private float mDeltaX;
82     private float mDeltaY;
83     private boolean mFinished;
84     private Interpolator mInterpolator;
87     private float mVelocity;
88     private float mCurrVelocity;
89     private int mDistance;

其中mMode代表Scroller的模式,FLING或者SCROLL;mStartX,mStartY代表起始的坐标;mFinalX、mFinalY代表重点坐标;mCurrX、mCurrY代表当前坐标;mDeltaX、mDeltaY代表需要移动的偏移量,mFinished代表是否完成;mInterpolator用来计算下一次移动的位置,可以用它来控制Scroller的移动效果。
在使用过程中,我们会调用scroller.startScroll()方法,startScroll()方法如下:

public void startScroll(int startX, int startY, int dx, int dy, int duration) {
399        mMode = SCROLL_MODE;    // 设置mMode为SCROLL_MODE模式
400        mFinished = false;      // 结束标记为false
401        mDuration = duration;   // 滚动的时间间隔
402        mStartTime = AnimationUtils.currentAnimationTimeMillis();    // 当前时间戳
403        mStartX = startX;    // 起始
404        mStartY = startY;
405        mFinalX = startX + dx;  // 结束
406        mFinalY = startY + dy;
407        mDeltaX = dx;     // 偏移量
408        mDeltaY = dy;
409        mDurationReciprocal = 1.0f / (float) mDuration;
410    }

这个方法进行一些初始化操作,给关键的变量赋值。同样fling()方法类似。

public void fling(int startX, int startY, int velocityX, int velocityY,
432            int minX, int maxX, int minY, int maxY) {
433        // Continue a scroll or fling in progress
434        if (mFlywheel && !mFinished) {
435            float oldVel = getCurrVelocity();
436
437            float dx = (float) (mFinalX - mStartX);
438            float dy = (float) (mFinalY - mStartY);
439            float hyp = FloatMath.sqrt(dx * dx + dy * dy);
440
441            float ndx = dx / hyp;
442            float ndy = dy / hyp;
443
444            float oldVelocityX = ndx * oldVel;
445            float oldVelocityY = ndy * oldVel;
446            if (Math.signum(velocityX) == Math.signum(oldVelocityX) &&
447                    Math.signum(velocityY) == Math.signum(oldVelocityY)) {
448                velocityX += oldVelocityX;
449                velocityY += oldVelocityY;
450            }
451        }
452
453        mMode = FLING_MODE;
454        mFinished = false;
455
456        float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY);
457     
458        mVelocity = velocity;
459        mDuration = getSplineFlingDuration(velocity);
460        mStartTime = AnimationUtils.currentAnimationTimeMillis();
461        mStartX = startX;
462        mStartY = startY;
463
464        float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;
465        float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;
466
467        double totalDistance = getSplineFlingDistance(velocity);
468        mDistance = (int) (totalDistance * Math.signum(velocity));
469        
470        mMinX = minX;
471        mMaxX = maxX;
472        mMinY = minY;
473        mMaxY = maxY;
474
475        mFinalX = startX + (int) Math.round(totalDistance * coeffX);
476        // Pin to mMinX <= mFinalX <= mMaxX
477        mFinalX = Math.min(mFinalX, mMaxX);
478        mFinalX = Math.max(mFinalX, mMinX);
479        
480        mFinalY = startY + (int) Math.round(totalDistance * coeffY);
481        // Pin to mMinY <= mFinalY <= mMaxY
482        mFinalY = Math.min(mFinalY, mMaxY);
483        mFinalY = Math.max(mFinalY, mMinY);
484    }

接着调用invalidate()导致view重绘,调用computeScroll(), 在computeScroll()方法里面需要调用Scroller.computeScrollOffset()方法,这个方法判断滚动是否结束,

public boolean computeScrollOffset() {
306        if (mFinished) {
307            return false;
308        }
309
310        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
311    
312        if (timePassed < mDuration) {
313            switch (mMode) {
314            case SCROLL_MODE:
315                float x = timePassed * mDurationReciprocal;
316    
317                if (mInterpolator == null)
318                    x = viscousFluid(x); 
319                else
320                    x = mInterpolator.getInterpolation(x);
321    
322                mCurrX = mStartX + Math.round(x * mDeltaX);
323                mCurrY = mStartY + Math.round(x * mDeltaY);
324                break;
325            case FLING_MODE:
326                final float t = (float) timePassed / mDuration;
327                final int index = (int) (NB_SAMPLES * t);
328                float distanceCoef = 1.f;
329                float velocityCoef = 0.f;
330                if (index < NB_SAMPLES) {
331                    final float t_inf = (float) index / NB_SAMPLES;
332                    final float t_sup = (float) (index + 1) / NB_SAMPLES;
333                    final float d_inf = SPLINE_POSITION[index];
334                    final float d_sup = SPLINE_POSITION[index + 1];
335                    velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
336                    distanceCoef = d_inf + (t - t_inf) * velocityCoef;
337                }
338
339                mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
340                
341                mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
342                // Pin to mMinX <= mCurrX <= mMaxX
343                mCurrX = Math.min(mCurrX, mMaxX);
344                mCurrX = Math.max(mCurrX, mMinX);
345                
346                mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
347                // Pin to mMinY <= mCurrY <= mMaxY
348                mCurrY = Math.min(mCurrY, mMaxY);
349                mCurrY = Math.max(mCurrY, mMinY);
350
351                if (mCurrX == mFinalX && mCurrY == mFinalY) {
352                    mFinished = true;
353                }
354
355                break;
356            }
357        }
358        else {
359            mCurrX = mFinalX;
360            mCurrY = mFinalY;
361            mFinished = true;
362        }
363        return true;
364    }

在这个方法里会去判断当前MODE类型,如果是滚动类型,计算一个x的权值,给mCurrX和mCurrY赋值,然后再调用View.scrollBy()或者View.scrollTo()方法来实现滚动。如果是Fling类型,类似滚动类型,根据速度和时间计算偏移量,赋值mCurrX, mCurrY,再调用View.scrollBy()或者View.scrollTo()实现滚动,接着调用invalidate(),继续重绘,从而实现滚动效果。

在Scroller类中有一个成员变量Interpolator,这个变量是用来描述滚动过程中的动画效果。他的调用时在computeScrollOffset()中,通过Interpolator.getInterpolation()来控制移动时的偏移量。如果Interpolator为null,则通过Scroll内部viscousFluid()来控制偏移量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值