zoukankan      html  css  js  c++  java
  • Android Scroller详解

    在学习使用Scroller之前,需要明白scrollTo()、scrollBy()方法。

    一、View的scrollTo()、scrollBy()

    scrollTo、scrollBy方法是View中的,因此任何的View都可以通过这两种方法进行移动。首先要明白的是,scrollTo、scrollBy滑动的是View中的内容(而且还是整体滑动),而不是View本身。我们的滑动控件如SrollView可以限定宽、高大小,以及在布局中的位置,但是滑动控件中的内容(或者里面的childView)可以是无限长、宽的,我们调用View的scrollTo、scrollBy方法,相当于是移动滑动控件中的画布Canvas,然后进行重绘,屏幕上也就显示相应的内容。如下:
    1、getScrollX()、getScrollY()
    在学习scrollTo()、scrollBy()之前,先来了解一下getScrollX()、getScrollY()方法。
    getScrollX()、getScrollY()得到的是偏移量,是相对自己初始位置的滑动偏移距离,只有当有scroll事件发生时,这两个方法才能有值,否则getScrollX()、getScrollY()都是初始时的值0,而不管你这个滑动控件在哪里。所谓自己初始位置是指,控件在刚开始显示时、没有滑动前的位置。以getScrollX()为例,其源码如下:
    public final int getScrollX() {
    return mScrollX;
    }
        可以看到getScrollX()直接返回的就是mScrollX,代表水平方向上的偏移量,getScrollY()也类似。偏移量mScrollX的正、负代表着,滑动控件中的内容相对于初始位置在水平方向上偏移情况,mScrollX为正代表着当前内容相对于初始位置向左偏移了mScrollX的距离,mScrollX为负表示当前内容相对于初始位置向右偏移了mScrollX的距离。
        这里的坐标系和我们平常的认知正好相反。为了以后更方便的处理滑动相关坐标和偏移,在处理偏移、滑动相关的功能时,我们就可以把坐标反过来看,如下图:
     
    因为滑动控件中的内容是整体进行滑动的,同时也是相对于自己显示时的初始位置的偏移,对于View中内容在偏移时的参考坐标原点(注意是内容视图的坐标原点,不是图中说的滑动控件的原点),可以选择初始位置的某一个地方,因为滑动时整体行为,在进行滑动的时候从这个选择的原点出进行分析即可。
     
    2、scrollTo()、scrollBy()
    scrollTo(int x,int y)移动的是View中的内容,而滑动控件中的内容都是整体移动的,scrollTo(int x,int y)中的参数表示View中的内容要相对于内容初始位置移动x和y的距离,即将内容移动到距离内容初始位置x和y的位置。正如前面所说,在处理偏移、滑动问题时坐标系和平常认知的坐标系是相反的。以一个例子说明scrollTo():
     
    说明:图中黄色矩形区域表示的是一个可滑动的View控件,绿色虚线矩形为滑动控件中的滑动内容。注意这里的坐标是相反的。(例子来源于:http://blog.csdn.net/bigconvience/article/details/26697645
     
    (1)调用scrollTo(100,0)表示将View中的内容移动到距离内容初始显示位置的x=100,y=0的地方,效果如下图:

    (2)调用scrollTo(0,100)效果如下图:

    (3)调用scrollTo(100,100)效果如下图:

    (4)调用scrollTo(-100,0)效果如下图:

    通过上面几个图,可以清楚看到scrollTo的作用和滑动坐标系的关系。在实际使用中,我们一般是在onTouchEvent()方法中处理滑动事件,在MotionEvent.ACTION_MOVE时调用scrollTo(int x,int y)进行滑动,在调用scrollTo(int x,int y)前,我们先要计算出两个参数值,即水平和垂直方向需要滑动的距离,如下:
    [java] view plain copy
    1. @Override  
    2. public boolean onTouchEvent(MotionEvent event) {  
    3.     int y = (int) event.getY();  
    4.     int action = event.getAction();  
    5.     switch (action){  
    6.     case MotionEvent.ACTION_DOWN:  
    7.             mLastY = y;  
    8.             break;  
    9.     case MotionEvent.ACTION_MOVE:  
    10.             int dy = mLastY - y;//本次手势滑动了多大距离  
    11.             int oldScrollY = getScrollY();//先计算之前已经偏移了多少距离  
    12.             int scrollY = oldScrollY + dy;//本次需要偏移的距离=之前已经偏移的距离+本次手势滑动了多大距离  
    13.             if(scrollY < 0){  
    14.                 scrollY = 0;  
    15.             }  
    16.             if(scrollY > getHeight() - mScreenHeight){  
    17.                 scrollY = getHeight() - mScreenHeight;  
    18.             }  
    19.             scrollTo(getScrollX(),scrollY);  
    20.             mLastY = y;  
    21.             break;  
    22.     }  
    23.     return true;  
    24. }  
    上面在计算参数时,分为了三步。第一是,通过int dy = mLastY - y;得到本次手势在屏幕上滑动了多少距离,这里要特别注意这个相减顺序,因为这里的坐标与平常是相反的,因此,手势滑动距离是按下时的坐标mLastY - 当前的坐标y;第二是,通过oldScrollY = getScrollY();获得滑动内容之前已经距初始位置便宜了多少;第三是,计算本次需要偏移的参数int scrollY = oldScrollY + dy; 后面通过两个if条件进行了边界处理,然后调用scrollTo进行滑动。调用完scrollTo后,新的偏移量又重新产生了。从scrollTo源码中可以看到:
    [java] view plain copy
    1. public void scrollTo(int x, int y) {  
    2.         if (mScrollX != x || mScrollY != y) {  
    3.             int oldX = mScrollX;  
    4.             int oldY = mScrollY;  
    5.             mScrollX = x;//赋值新的x偏移量  
    6.             mScrollY = y;//赋值新的y偏移量  
    7.             invalidateParentCaches();  
    8.             onScrollChanged(mScrollX, mScrollY, oldX, oldY);  
    9.             if (!awakenScrollBars()) {  
    10.                 postInvalidateOnAnimation();  
    11.             }  
    12.         }  
    13.     }  
    scrollTo是相对于初始位置来进行移动的,而scrollBy(int x ,int y)则是相对于上一次移动的距离来进行本次移动。scrollBy其实还是依赖于scrollTo的,如下源码:
    [java] view plain copy
    1. public void scrollBy(int x, int y) {  
    2.         scrollTo(mScrollX + x, mScrollY + y);  
    3.     }  
    可以看到,使用scrollBy其实就是省略了我们在计算scrollTo参数时的第三步而已,因为scrollBy内部已经自己帮我加上了第三步的计算。因此scrollBy的作用就是相当于在上一次的偏移情况下进行本次的偏移。
     
    一个完整的水平方向滑动的例子:
    [java] view plain copy
    1. public class MyViewPager extends ViewGroup {  
    2.   
    3.     private int mLastX;  
    4.   
    5.     public MyViewPager(Context context) {  
    6.         super(context);  
    7.         init(context);  
    8.     }  
    9.   
    10.     public MyViewPager(Context context, AttributeSet attrs) {  
    11.         super(context, attrs);  
    12.         init(context);  
    13.     }  
    14.   
    15.     public MyViewPager(Context context, AttributeSet attrs, int defStyleAttr) {  
    16.         super(context, attrs, defStyleAttr);  
    17.         init(context);  
    18.     }  
    19.   
    20.     private void init(Context context) {  
    21.   
    22.     }  
    23.   
    24.     @Override  
    25.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    26.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
    27.         int count = getChildCount();  
    28.         for(int i = 0; i < count; i++){  
    29.             View child = getChildAt(i);  
    30.             child.measure(widthMeasureSpec,heightMeasureSpec);  
    31.         }  
    32.     }  
    33.   
    34.     @Override  
    35.     protected void onLayout(boolean changed, int l, int t, int r, int b) {  
    36.         int count = getChildCount();  
    37.         Log.d("TAG","--l-->"+l+",--t-->"+t+",-->r-->"+r+",--b-->"+b);  
    38.         for(int i = 0; i < count; i++){  
    39.             View child = getChildAt(i);  
    40.             child.layout(i * getWidth(), t, (i+1) * getWidth(), b);  
    41.         }  
    42.     }  
    43.   
    44.     @Override  
    45.     public boolean onTouchEvent(MotionEvent ev) {  
    46.         int x = (int) ev.getX();  
    47.         switch (ev.getAction()){  
    48.             case MotionEvent.ACTION_DOWN:  
    49.                 mLastX = x;  
    50.                 break;  
    51.             case MotionEvent.ACTION_MOVE:  
    52.                 int dx = mLastX - x;  
    53.                 int oldScrollX = getScrollX();//原来的偏移量  
    54.                 int preScrollX = oldScrollX + dx;//本次滑动后形成的偏移量  
    55.                 if(preScrollX > (getChildCount() - 1) * getWidth()){  
    56.                     preScrollX = (getChildCount() - 1) * getWidth();  
    57.                 }  
    58.                 if(preScrollX < 0){  
    59.                     preScrollX = 0;  
    60.                 }  
    61.                 scrollTo(preScrollX,getScrollY());  
    62.                 mLastX = x;  
    63.                 break;  
    64.         }  
    65.         return true;  
    66.     }  
    67. }  
    布局文件:
    [html] view plain copy
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    3. android:layout_width="match_parent"  
    4. android:layout_height="match_parent"  
    5. android:orientation="vertical">  
    6.   
    7.     <com.scu.lly.viewtest.view.MyViewPager  
    8. android:layout_width="match_parent"  
    9. android:layout_height="300dp"  
    10. >  
    11.         <ImageView  
    12. android:layout_width="match_parent"  
    13. android:layout_height="match_parent"  
    14. android:scaleType="fitXY"  
    15. android:src="@drawable/test1" />  
    16.   
    17.         <ImageView  
    18. android:layout_width="match_parent"  
    19. android:layout_height="match_parent"  
    20. android:scaleType="fitXY"  
    21. android:src="@drawable/test2" />  
    22.   
    23.         <ImageView  
    24. android:layout_width="match_parent"  
    25. android:layout_height="match_parent"  
    26. android:scaleType="fitXY"  
    27. android:src="@drawable/test3" />  
    28.   
    29.         <ImageView  
    30. android:layout_width="match_parent"  
    31. android:layout_height="match_parent"  
    32. android:scaleType="fitXY"  
    33. android:src="@drawable/test4" />  
    34.     </com.scu.lly.viewtest.view.MyViewPager>  
    35. </LinearLayout>  
    效果如图:
     

    二、Scroller滑动辅助类

    根据我们上面的分析,可知View的scrollTo()、scrollBy()是瞬间完成的,当我们的手指在屏幕上移动时,内容会跟着手指滑动,但是当我们手指一抬起时,滑动就会停止,如果我们想要有一种惯性的滚动过程效果和回弹效果,此时就需要使用Scroller辅助类。
    但是注意的是,Scroller本身不会去移动View,它只是一个移动计算辅助类,用于跟踪控件滑动的轨迹,只相当于一个滚动轨迹记录工具,最终还是通过View的scrollTo、scrollBy方法完成View的移动的。
    在使用Scroller类之前,先了解其重要的两个方法:
    (1)startScroll()
    public void startScroll(int startX, int startY, int dx, int dy, int duration)
    开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位,即到达偏移坐标为(startX+dx , startY+dy)处。
    (2)computeScrollOffset()
    public boolean computeScrollOffset()
    滑动过程中,根据当前已经消逝的时间计算当前偏移的坐标点,保存在mCurrX和mCurrY值中。
    上面两个方法的源码如下:
    public class Scroller {
    private int mStartX;//水平方向,滑动时的起点偏移坐标
    private int mStartY;//垂直方向,滑动时的起点偏移坐标
    private int mFinalX;//滑动完成后的偏移坐标,水平方向
    private int mFinalY;//滑动完成后的偏移坐标,垂直方向

    private int mCurrX;//滑动过程中,根据消耗的时间计算出的当前的滑动偏移距离,水平方向
    private int mCurrY;//滑动过程中,根据消耗的时间计算出的当前的滑动偏移距离,垂直方向
    private int mDuration; //本次滑动的动画时间
    private float mDeltaX;//滑动过程中,在达到mFinalX前还需要滑动的距离,水平方向
    private float mDeltaY;//滑动过程中,在达到mFinalX前还需要滑动的距离,垂直方向

    public void startScroll(int startX, int startY, int dx, int dy) {
    startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
    }

    /**
    * 开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位,即到达偏移坐标为(startX+dx , startY+dy)处
    */
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
    mMode = SCROLL_MODE;
    mFinished = false;
    mDuration = duration;
    mStartTime = AnimationUtils.currentAnimationTimeMillis();
    mStartX = startX;
    mStartY = startY;
    mFinalX = startX + dx;//确定本次滑动完成后的偏移坐标
    mFinalY = startY + dy;
    mDeltaX = dx;
    mDeltaY = dy;
    mDurationReciprocal = 1.0f / (float) mDuration;
    }

    /**
    * 滑动过程中,根据当前已经消逝的时间计算当前偏移的坐标点,保存在mCurrX和mCurrY值中
    * @return
    */
    public boolean computeScrollOffset() {
    if (mFinished) {//已经完成了本次动画控制,直接返回为false
    return false;
    }
    int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
    if (timePassed < mDuration) {
    switch (mMode) {
    case SCROLL_MODE:
    final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
    mCurrX = mStartX + Math.round(x * mDeltaX);//计算出当前的滑动偏移位置,x轴
    mCurrY = mStartY + Math.round(x * mDeltaY);//计算出当前的滑动偏移位置,y轴
    break;
    ...
    }
    }else {
    mCurrX = mFinalX;
    mCurrY = mFinalY;
    mFinished = true;
    }
    return true;
    }
    ...
    }
    Scroller类中最重要的两个方法就是startScroll()和computeScrollOffset(),但是Scroller类只是一个滑动计算辅助类,它的startScroll()和computeScrollOffset()方法中也只是对一些轨迹参数进行设置和计算,真正需要进行滑动还是得通过View的scrollTo()、scrollBy()方法。为此,View中提供了computeScroll()方法来控制这个滑动流程。computeScroll()方法会在绘制子视图的时候进行调用。其源码如下:
    [java] view plain copy
    1. /**  
    2.  * Called by a parent to request that a child update its values for mScrollX  
    3.  * and mScrollY if necessary. This will typically be done if the child is  
    4.  * animating a scroll using a {@link android.widget.Scroller Scroller}  
    5.  * object.  
    6.  * 由父视图调用用来请求子视图根据偏移值 mScrollX,mScrollY重新绘制   
    7.  */  
    8. public void computeScroll() { //空方法 ,自定义滑动功能的ViewGroup必须实现方法体    
    9.         
    10. }   
    因此Scroller类的基本使用流程可以总结如下:
    (1)首先通过Scroller类的startScroll()开始一个滑动动画控制,里面进行了一些轨迹参数的设置和计算;
    (2)在调用startScroll()的后面调用invalidate();引起视图的重绘操作,从而触发ViewGroup中的computeScroll()被调用;
    (3)在computeScroll()方法中,先调用Scroller类中的computeScrollOffset()方法,里面根据当前消耗时间进行轨迹坐标的计算,然后取得计算出的当前滑动的偏移坐标,调用View的scrollTo()方法进行滑动控制,最后也需要调用invalidate();进行重绘。
    如下的一个简单代码示例:   
    [java] view plain copy
    1. @Override  
    2.    public boolean onTouchEvent(MotionEvent ev) {  
    3.        initVelocityTrackerIfNotExists();  
    4.        mVelocityTracker.addMovement(ev);  
    5.        int x = (int) ev.getX();  
    6.        switch (ev.getAction()){  
    7.            case MotionEvent.ACTION_DOWN:  
    8.                if(!mScroller.isFinished()){  
    9.                    mScroller.abortAnimation();  
    10.                }  
    11.                mLastX = x;  
    12.                break;  
    13.            case MotionEvent.ACTION_MOVE:  
    14.                int dx = mLastX - x;  
    15.                int oldScrollX = getScrollX();//原来的偏移量  
    16.                int preScrollX = oldScrollX + dx;//本次滑动后形成的偏移量  
    17.                if(preScrollX > (getChildCount() - 1) * getWidth()){  
    18.                    preScrollX = (getChildCount() - 1) * getWidth();  
    19.                }  
    20.                if(preScrollX < 0){  
    21.                    preScrollX = 0;  
    22.                }  
    23.                //开始滑动动画      
    24.                mScroller.startScroll(mScroller.getFinalX(),mScroller.getFinalY(),dx,0);//第一步  
    25.                //注意,一定要进行invalidate刷新界面,触发computeScroll()方法,因为单纯的startScroll()是属于Scroller的,只是一个辅助类,并不会触发界面的绘制  
    26.                invalidate();  
    27.                mLastX = x;  
    28.                break;  
    29.        }  
    30.        return true;  
    31.    }  
    32.   
    33.    @Override  
    34.    public void computeScroll() {  
    35.        super.computeScroll();  
    36.        if(mScroller.computeScrollOffset()){//第二步  
    37.            scrollTo(mScroller.getCurrX(),mScroller.getCurrY());//第三步  
    38.            invalidate();  
    39.        }  
    40.    }  
     
    下面是一个完整的例子:一个类似ViewPager的Demo,效果图如下:
    代码如下:
    [java] view plain copy
    1. public class MyViewPager3 extends ViewGroup {  
    2.   
    3.     private int mLastX;  
    4.   
    5.     private Scroller mScroller;  
    6.     private VelocityTracker mVelocityTracker;  
    7.     private int mTouchSlop;  
    8.     private int mMaxVelocity;  
    9.     /** 
    10.      * 当前显示的是第几个屏幕 
    11.      */  
    12.     private int mCurrentPage = 0;  
    13.   
    14.     public MyViewPager3(Context context) {  
    15.         super(context);  
    16.         init(context);  
    17.     }  
    18.   
    19.     public MyViewPager3(Context context, AttributeSet attrs) {  
    20.         super(context, attrs);  
    21.         init(context);  
    22.     }  
    23.   
    24.     public MyViewPager3(Context context, AttributeSet attrs, int defStyleAttr) {  
    25.         super(context, attrs, defStyleAttr);  
    26.         init(context);  
    27.     }  
    28.   
    29.     private void init(Context context) {  
    30.         mScroller = new Scroller(context);  
    31.         ViewConfiguration config = ViewConfiguration.get(context);  
    32.         mTouchSlop = config.getScaledPagingTouchSlop();  
    33.         mMaxVelocity = config.getScaledMinimumFlingVelocity();  
    34.     }  
    35.   
    36.     @Override  
    37.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    38.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
    39.         int count = getChildCount();  
    40.         for(int i = 0; i < count; i++){  
    41.             View child = getChildAt(i);  
    42.             child.measure(widthMeasureSpec, heightMeasureSpec);  
    43.         }  
    44.     }  
    45.   
    46.     @Override  
    47.     protected void onLayout(boolean changed, int l, int t, int r, int b) {  
    48.         int count = getChildCount();  
    49.         Log.d("TAG","--l-->"+l+",--t-->"+t+",-->r-->"+r+",--b-->"+b);  
    50.         for(int i = 0; i < count; i++){  
    51.             View child = getChildAt(i);  
    52.             child.layout(i * getWidth(), t, (i + 1) * getWidth(), b);  
    53.         }  
    54.     }  
    55.   
    56.     @Override  
    57.     public boolean onTouchEvent(MotionEvent ev) {  
    58.         initVelocityTrackerIfNotExists();  
    59.         mVelocityTracker.addMovement(ev);  
    60.         int x = (int) ev.getX();  
    61.         switch (ev.getAction()){  
    62.             case MotionEvent.ACTION_DOWN:  
    63.                 if(!mScroller.isFinished()){  
    64.                     mScroller.abortAnimation();  
    65.                 }  
    66.                 mLastX = x;  
    67.                 break;  
    68.             case MotionEvent.ACTION_MOVE:  
    69.                 int dx = mLastX - x;  
    70.                 /* 注释的里面是使用startScroll()来进行滑动的 
    71.                 int oldScrollX = getScrollX();//原来的偏移量 
    72.                 int preScrollX = oldScrollX + dx;//本次滑动后形成的偏移量 
    73.                 if (preScrollX > (getChildCount() - 1) * getWidth()) { 
    74.                     preScrollX = (getChildCount() - 1) * getWidth(); 
    75.                     dx = preScrollX - oldScrollX; 
    76.                 } 
    77.                 if (preScrollX < 0) { 
    78.                     preScrollX = 0; 
    79.                     dx = preScrollX - oldScrollX; 
    80.                 } 
    81.                 mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, 0); 
    82.                 //注意,使用startScroll后面一定要进行invalidate刷新界面,触发computeScroll()方法,因为单纯的startScroll()是属于Scroller的,只是一个辅助类,并不会触发界面的绘制 
    83.                 invalidate(); 
    84.                 */  
    85.                 //但是一般在ACTION_MOVE中我们直接使用scrollTo或者scrollBy更加方便  
    86.                 scrollBy(dx,0);  
    87.                 mLastX = x;  
    88.                 break;  
    89.             case MotionEvent.ACTION_UP:  
    90.                 final VelocityTracker velocityTracker = mVelocityTracker;  
    91.                 velocityTracker.computeCurrentVelocity(1000);  
    92.                 int initVelocity = (int) velocityTracker.getXVelocity();  
    93.                 if(initVelocity > mMaxVelocity && mCurrentPage > 0){//如果是快速的向右滑,则需要显示上一个屏幕  
    94.                     Log.d("TAG","----------------快速的向右滑--------------------");  
    95.                     scrollToPage(mCurrentPage - 1);  
    96.                 }else if(initVelocity < -mMaxVelocity && mCurrentPage < (getChildCount() - 1)){//如果是快速向左滑动,则需要显示下一个屏幕  
    97.                     Log.d("TAG","----------------快速的向左滑--------------------");  
    98.                     scrollToPage(mCurrentPage + 1);  
    99.                 }else{//不是快速滑动的情况,此时需要计算是滑动到  
    100.                     Log.d("TAG","----------------慢慢的滑动--------------------");  
    101.                     slowScrollToPage();  
    102.                 }  
    103.                 recycleVelocityTracker();  
    104.                 break;  
    105.         }  
    106.         return true;  
    107.     }  
    108.   
    109.     /** 
    110.      * 缓慢滑动抬起手指的情形,需要判断是停留在本Page还是往前、往后滑动 
    111.      */  
    112.     private void slowScrollToPage() {  
    113.         //当前的偏移位置  
    114.         int scrollX = getScrollX();  
    115.         int scrollY = getScrollY();  
    116.         //判断是停留在本Page还是往前一个page滑动或者是往后一个page滑动  
    117.         int whichPage = (getScrollX() + getWidth() / 2 ) / getWidth() ;  
    118.         scrollToPage(whichPage);  
    119.     }  
    120.   
    121.     /** 
    122.      * 滑动到指定屏幕 
    123.      * @param indexPage 
    124.      */  
    125.     private void scrollToPage(int indexPage) {  
    126.         mCurrentPage = indexPage;  
    127.         if(mCurrentPage > getChildCount() - 1){  
    128.             mCurrentPage = getChildCount() - 1;  
    129.         }  
    130.         //计算滑动到指定Page还需要滑动的距离  
    131.         int dx = mCurrentPage * getWidth() - getScrollX();  
    132.         mScroller.startScroll(getScrollX(),0,dx,0,Math.abs(dx) * 2);//动画时间设置为Math.abs(dx) * 2 ms  
    133.         //记住,使用Scroller类需要手动invalidate  
    134.         invalidate();  
    135.     }  
    136.   
    137.     @Override  
    138.     public void computeScroll() {  
    139.         Log.d("TAG""---------computeScrollcomputeScrollcomputeScroll--------------");  
    140.         super.computeScroll();  
    141.         if(mScroller.computeScrollOffset()){  
    142.             scrollTo(mScroller.getCurrX(),mScroller.getCurrY());  
    143.             invalidate();  
    144.         }  
    145.     }  
    146.   
    147.     private void recycleVelocityTracker() {  
    148.         if (mVelocityTracker != null) {  
    149.             mVelocityTracker.recycle();  
    150.             mVelocityTracker = null;  
    151.         }  
    152.     }  
    153.   
    154.     private void initVelocityTrackerIfNotExists() {  
    155.         if(mVelocityTracker == null){  
    156.             mVelocityTracker = VelocityTracker.obtain();  
    157.         }  
    158.     }  
    159.   
    160. }  
    布局文件如下:
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.lusheep.viewtest.view.MyViewPager3
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:background="#999" >
    <ImageView
    android:layout_width="300dp"
    android:layout_height="match_parent"
    android:scaleType="fitXY"
    android:src="@drawable/test1" />

    <ImageView
    android:layout_width="300dp"
    android:layout_height="match_parent"
    android:scaleType="fitXY"
    android:src="@drawable/test2" />

    <ImageView
    android:layout_width="300dp"
    android:layout_height="match_parent"
    android:scaleType="fitXY"
    android:src="@drawable/test3" />

    <ImageView
    android:layout_width="300dp"
    android:layout_height="match_parent"
    android:scaleType="fitXY"
    android:src="@drawable/test4" />
    </com.lusheep.viewtest.view.MyViewPager3>
    </LinearLayout>
     
     
    简单总结:
    (1)Scroller类能够帮助我们实现高级的滑动功能,如手指抬起后的惯性滑动功能。使用流程为,首先通过Scroller类的startScroll()+invalidate()触发View的computeScroll(),在computeScroll()中让Scroller类去计算最新的坐标信息,拿到最新的坐标偏移信息后还是要调用View的scrollTo来实现滑动。可以看到,使用Scroller的整个流程比较简单,关键的是控制滑动的一些逻辑计算,比如上面例子中的计算什么时候该往哪一页滑动...
    (2)Android后面推出了OverScroller类,OverScroller在整体功能上和Scroller类似,使用也相同。OverScroller类可以完全代替Scroller,相比Scroller,OverScroller主要是增加了对滑动到边界的一些控制,如增加一些回弹效果等,功能更加强大。
  • 相关阅读:
    2016/9/18结对编程之需求分析与原型设计。
    K米评测
    软件工程的实践项目课程的自我目标
    url学习1
    调研构建之法指导下的作品
    初次尝试对接
    第二次结对编程作业——毕业导师智能匹配
    uml
    Qt中采用cairo将图片导出至PDF
    SQL删除重复的记录(只保留一条)
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/11982627.html
Copyright © 2011-2022 走看看