在编写自定义滑动控件时常常会用到Android触摸机制和Scroller及VelocityTracker。Android Touch系统简介(二):实例详解onInterceptTouchEvent与onTouchEvent的调用过程对Android触摸机制需要用到的函数进行了详细的解释,本文主要介绍两个重要的类:Scroller及VelocityTracker。利用上述知识,最后给出了一个自定义滑动控件的demo,该demo类似于ImageGallery。ImageGallery一般是用GridView来实现的,可以左右滑动。本例子实现的控件直接继承一个ViewGroup,对其回调函数如 onTouchEvent、onInterceptTouchEvent、computeScroll等进行重载。弄懂该代码,对Android touch的认识将会更深一层。

VelocityTracker:用于对触摸点的速度跟踪,方便获取触摸点的速度。
用法:一般在onTouchEvent事件中被调用,先在down事件中获取一个VecolityTracker对象,然后在move或up事件中获取速度,调用流程可如下列所示:

[java] view plain copy
  1. VelocityTrackervTracker=null
  2. @Override
  3. publicbooleanonTouchEvent(MotionEventevent){
  4. intaction=event.getAction();
  5. switch(action){
  6. caseMotionEvent.ACTION_DOWN:
  7. if(vTracker==null){
  8. vTracker=VelocityTracker.obtain();
  9. }else{
  10. vTracker.clear();
  11. }
  12. vTracker.addMovement(event);
  13. break;
  14. caseMotionEvent.ACTION_MOVE:
  15. vTracker.addMovement(event);
  16. //设置单位,1000表示每秒多少像素(pix/second),1代表每微秒多少像素(pix/millisecond)。
  17. vTracker.computeCurrentVelocity(1000);
  18. //从左向右划返回正数,从右向左划返回负数
  19. System.out.println("thexvelocityis"+vTracker.getXVelocity());
  20. //从上往下划返回正数,从下往上划返回负数
  21. System.out.println("theyvelocityis"+vTracker.getYVelocity());
  22. break;
  23. caseMotionEvent.ACTION_UP:
  24. caseMotionEvent.ACTION_CANCEL:
  25. vTracker.recycle();
  26. break;
  27. }
  28. returntrue;
  29. }


Scroller:用于跟踪控件滑动的轨迹,此类不会移动控件,需要你在View的一个回调函数computerScroll()中使用Scroller对象还获取滑动的数据来控制某个View。

[java] view plain copy
  1. /**
  2. *CalledbyaparenttorequestthatachildupdateitsvaluesformScrollX
  3. *andmScrollYifnecessary.Thiswilltypicallybedoneifthechildis
  4. *animatingascrollusinga{@linkandroid.widget.ScrollerScroller}
  5. *object.
  6. */
  7. publicvoidcomputeScroll()
  8. {
  9. }
parentView在绘制式,会调用dispatchDraw(Canvas canvas),该函数会调用ViewGroup中的每个子view的boolean draw(Canvas canvas, ViewGroup parent, long drawingTime),用户绘制View,此函数在绘制View的过程中会调用computeScroll()
下面给出一段代码:
[java] view plain copy
  1. @Override
  2. publicvoidcomputeScroll(){
  3. //TODOAuto-generatedmethodstub
  4. Log.e(TAG,"computeScroll");
  5. if(mScroller.computeScrollOffset()){//or!mScroller.isFinished()
  6. Log.e(TAG,mScroller.getCurrX()+"======"+mScroller.getCurrY());
  7. scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
  8. Log.e(TAG,"###getleftis"+getLeft()+"###getRightis"+getRight());
  9. postInvalidate();
  10. }
  11. else
  12. Log.i(TAG,"havedonethescoller-----");
  13. }
这段代码在滑动view之前先调用mScroller.computeScrollOffset()来判断滑动动画是否已结束。computerScrollerOffset()的源代码如下:

[java] view plain copy
  1. /**
  2. *Callthiswhenyouwanttoknowthenewlocation.Ifitreturnstrue,
  3. *theanimationisnotyetfinished.
  4. */
  5. publicbooleancomputeScrollOffset(){
  6. if(mFinished){
  7. returnfalse;
  8. }
  9. //滑动已经持续的时间
  10. inttimePassed=(int)(AnimationUtils.currentAnimationTimeMillis()-mStartTime);
  11. //若在规定时间还未用完,则继续设置新的滑动位置mCurrX和mCurry
  12. if(timePassed<mDuration){
  13. switch(mMode){
  14. caseSCROLL_MODE:
  15. floatx=timePassed*mDurationReciprocal;
  16. if(mInterpolator==null)
  17. x=viscousFluid(x);
  18. else
  19. x=mInterpolator.getInterpolation(x);
  20. mCurrX=mStartX+Math.round(x*mDeltaX);
  21. mCurrY=mStartY+Math.round(x*mDeltaY);
  22. break;
  23. caseFLING_MODE:
  24. finalfloatt=(float)timePassed/mDuration;
  25. finalintindex=(int)(NB_SAMPLES*t);
  26. floatdistanceCoef=1.f;
  27. floatvelocityCoef=0.f;
  28. if(index<NB_SAMPLES){
  29. finalfloatt_inf=(float)index/NB_SAMPLES;
  30. finalfloatt_sup=(float)(index+1)/NB_SAMPLES;
  31. finalfloatd_inf=SPLINE_POSITION[index];
  32. finalfloatd_sup=SPLINE_POSITION[index+1];
  33. velocityCoef=(d_sup-d_inf)/(t_sup-t_inf);
  34. distanceCoef=d_inf+(t-t_inf)*velocityCoef;
  35. }
  36. mCurrVelocity=velocityCoef*mDistance/mDuration*1000.0f;
  37. mCurrX=mStartX+Math.round(distanceCoef*(mFinalX-mStartX));
  38. //PintomMinX<=mCurrX<=mMaxX
  39. mCurrX=Math.min(mCurrX,mMaxX);
  40. mCurrX=Math.max(mCurrX,mMinX);
  41. mCurrY=mStartY+Math.round(distanceCoef*(mFinalY-mStartY));
  42. //PintomMinY<=mCurrY<=mMaxY
  43. mCurrY=Math.min(mCurrY,mMaxY);
  44. mCurrY=Math.max(mCurrY,mMinY);
  45. if(mCurrX==mFinalX&&mCurrY==mFinalY){
  46. mFinished=true;
  47. }
  48. break;
  49. }
  50. }
  51. else{
  52. mCurrX=mFinalX;
  53. mCurrY=mFinalY;
  54. mFinished=true;
  55. }
  56. returntrue;
  57. }
ViewGroup.computeScroll()被调用时机:
当我们执行ontouch或invalidate()或postInvalidate()都会导致这个方法的执行。

我们在开发控件时,常会有这样的需求:当单机某个按钮时,某个图片会在规定的时间内滑出窗口,而不是一下子进入窗口。实现这个功能可以使用Scroller来实现。
下面给出一段代码,该代码控制下一个界面在3秒时间内缓慢进入的效果。

[java] view plain copy
  1. publicvoidmoveToRightSide(){
  2. if(curScreen<=0){
  3. return;
  4. }
  5. curScreen--;
  6. Log.i(TAG,"----moveToRightSide----curScreen"+curScreen);
  7. mScroller.startScroll((curScreen+1)*getWidth(),0,-getWidth(),0,3000);
  8. scrollTo(curScreen*getWidth(),0);
  9. invalidate();
  10. }
上述代码用到了一个函数:void android.widget.Scroller.startScroll(int startX, int startY, int dx, int dy, int duration)
当startScroll执行过程中即在duration时间内,computeScrollOffset 方法会一直返回true,但当动画执行完成后会返回返加false.
这个函数的源码如下所示,主要用于设置滑动参数

[java] view plain copy
  1. /**
  2. *Startscrollingbyprovidingastartingpoint,thedistancetotravel,
  3. *andthedurationofthescroll.
  4. *
  5. *@paramstartXStartinghorizontalscrolloffsetinpixels.Positive
  6. *numberswillscrollthecontenttotheleft.
  7. *@paramstartYStartingverticalscrolloffsetinpixels.Positivenumbers
  8. *willscrollthecontentup.
  9. *@paramdxHorizontaldistancetotravel.Positivenumberswillscrollthe
  10. *contenttotheleft.
  11. *@paramdyVerticaldistancetotravel.Positivenumberswillscrollthe
  12. *contentup.
  13. *@paramdurationDurationofthescrollinmilliseconds.
  14. */
  15. publicvoidstartScroll(intstartX,intstartY,intdx,intdy,intduration){
  16. mMode=SCROLL_MODE;
  17. mFinished=false;
  18. mDuration=duration;
  19. mStartTime=AnimationUtils.currentAnimationTimeMillis();
  20. mStartX=startX;
  21. mStartY=startY;
  22. mFinalX=startX+dx;
  23. mFinalY=startY+dy;
  24. mDeltaX=dx;
  25. mDeltaY=dy;
  26. mDurationReciprocal=1.0f/(float)mDuration;
  27. }

invalidate()会使得视图重绘,导致parent调用了dispatchDraw(Canvas canvas),然后递归调用child View的draw()函数,该函数又会调用我们定义的computeScroll(), 而这个函数又会调用mScroller.computeScrollOffset()判断动画是否结束,若没结束则继续重绘直到直到startScroll中设置的时间耗尽mScroller.computeScrollOffset()返回false才停下来。


public void snapToScreen(int whichScreen) {            // get the valid layout page            whichScreen = Math.max(0, Math.min(whichScreen, getChildCount()-1));            if (getScrollX() != (whichScreen*getWidth())) {                                final int delta = whichScreen*getWidth()-getScrollX();                //当startScroll执行过程中即在duration时间内,                // computeScrollOffset  方法会一直返回true,                // 但当动画执行完成后会返回返加false.                  mScroller.startScroll(getScrollX(), 0,                         delta, 0, 300);                        mCurScreen = whichScreen;                ///invalidate()会使得视图重绘,导致parent调用了dispatchDraw(Canvas canvas),                ///然后递归调用child View的draw()函数,该函数又会调用我们定义的computeScroll(),                ///而这个函数又会调用mScroller.computeScrollOffset()判断动画是否结束,                ///若没结束则继续重绘直到直到startScroll中设置的时间耗尽                ///mScroller.computeScrollOffset()返回false才停下来。            invalidate();       // Redraw the layout                            if (mOnViewChangeListener != null)            {            mOnViewChangeListener.OnViewChange(mCurScreen);            }        }        }        ///ViewGroup.computeScroll()被调用时机:    ///当我们执行ontouch或invalidate()或postInvalidate()都会导致这个方法的执行。@Overridepublic void computeScroll() {// TODO Auto-generated method stubif (mScroller.computeScrollOffset()) {                scrollTo(mScroller.getCurrX(), mScroller.getCurrY());            postInvalidate();        }   }


更多相关文章

  1. Android 控件七 ImageView 控件
  2. Android SQLite数据库 《第一行代码》
  3. Android简明开发教程二十四:总结及示例代码下载
  4. 如何获取android源代码
  5. 自定义控件
  6. 代码讲解Android Scroller、VelocityTracker
  7. 基于ffmpeg的Android播放器开源代码

随机推荐

  1. (三)Android(安卓)项目在Eclipse中的目录结
  2. 摩托罗拉公司在开发Android应用商店 – S
  3. Android应用程序通用自动脱壳方法研究
  4. UC/OS-II学习 2
  5. 【Android开发学习13】Android(安卓)Open
  6. 使用wireshark 动态显示Android应用中的
  7. Android智能指针 (sp & wp)
  8. android 广播的使用
  9. IC卡读卡器在安卓(android)下的开发
  10. Android事件分发机制完全解析,带你从源码