Android Scroller、VelocityTracker
在编写自定义滑动控件时常常会用到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
- VelocityTrackervTracker=null;
- @Override
- publicbooleanonTouchEvent(MotionEventevent){
- intaction=event.getAction();
- switch(action){
- caseMotionEvent.ACTION_DOWN:
- if(vTracker==null){
- vTracker=VelocityTracker.obtain();
- }else{
- vTracker.clear();
- }
- vTracker.addMovement(event);
- break;
- caseMotionEvent.ACTION_MOVE:
- vTracker.addMovement(event);
- //设置单位,1000表示每秒多少像素(pix/second),1代表每微秒多少像素(pix/millisecond)。
- vTracker.computeCurrentVelocity(1000);
- //从左向右划返回正数,从右向左划返回负数
- System.out.println("thexvelocityis"+vTracker.getXVelocity());
- //从上往下划返回正数,从下往上划返回负数
- System.out.println("theyvelocityis"+vTracker.getYVelocity());
- break;
- caseMotionEvent.ACTION_UP:
- caseMotionEvent.ACTION_CANCEL:
- vTracker.recycle();
- break;
- }
- returntrue;
- }
Scroller:用于跟踪控件滑动的轨迹,此类不会移动控件,需要你在View的一个回调函数computerScroll()中使用Scroller对象还获取滑动的数据来控制某个View。
[java] view plain copy
- /**
- *CalledbyaparenttorequestthatachildupdateitsvaluesformScrollX
- *andmScrollYifnecessary.Thiswilltypicallybedoneifthechildis
- *animatingascrollusinga{@linkandroid.widget.ScrollerScroller}
- *object.
- */
- publicvoidcomputeScroll()
- {
- }
下面给出一段代码:
[java] view plain copy
- @Override
- publicvoidcomputeScroll(){
- //TODOAuto-generatedmethodstub
- Log.e(TAG,"computeScroll");
- if(mScroller.computeScrollOffset()){//or!mScroller.isFinished()
- Log.e(TAG,mScroller.getCurrX()+"======"+mScroller.getCurrY());
- scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
- Log.e(TAG,"###getleftis"+getLeft()+"###getRightis"+getRight());
- postInvalidate();
- }
- else
- Log.i(TAG,"havedonethescoller-----");
- }
[java] view plain copy
- /**
- *Callthiswhenyouwanttoknowthenewlocation.Ifitreturnstrue,
- *theanimationisnotyetfinished.
- */
- publicbooleancomputeScrollOffset(){
- if(mFinished){
- returnfalse;
- }
- //滑动已经持续的时间
- inttimePassed=(int)(AnimationUtils.currentAnimationTimeMillis()-mStartTime);
- //若在规定时间还未用完,则继续设置新的滑动位置mCurrX和mCurry
- if(timePassed<mDuration){
- switch(mMode){
- caseSCROLL_MODE:
- floatx=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;
- caseFLING_MODE:
- finalfloatt=(float)timePassed/mDuration;
- finalintindex=(int)(NB_SAMPLES*t);
- floatdistanceCoef=1.f;
- floatvelocityCoef=0.f;
- if(index<NB_SAMPLES){
- finalfloatt_inf=(float)index/NB_SAMPLES;
- finalfloatt_sup=(float)(index+1)/NB_SAMPLES;
- finalfloatd_inf=SPLINE_POSITION[index];
- finalfloatd_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));
- //PintomMinX<=mCurrX<=mMaxX
- mCurrX=Math.min(mCurrX,mMaxX);
- mCurrX=Math.max(mCurrX,mMinX);
- mCurrY=mStartY+Math.round(distanceCoef*(mFinalY-mStartY));
- //PintomMinY<=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;
- }
- returntrue;
- }
当我们执行ontouch或invalidate()或postInvalidate()都会导致这个方法的执行。
我们在开发控件时,常会有这样的需求:当单机某个按钮时,某个图片会在规定的时间内滑出窗口,而不是一下子进入窗口。实现这个功能可以使用Scroller来实现。
下面给出一段代码,该代码控制下一个界面在3秒时间内缓慢进入的效果。
[java] view plain copy
- publicvoidmoveToRightSide(){
- if(curScreen<=0){
- return;
- }
- curScreen--;
- Log.i(TAG,"----moveToRightSide----curScreen"+curScreen);
- mScroller.startScroll((curScreen+1)*getWidth(),0,-getWidth(),0,3000);
- scrollTo(curScreen*getWidth(),0);
- invalidate();
- }
当startScroll执行过程中即在duration时间内,computeScrollOffset 方法会一直返回true,但当动画执行完成后会返回返加false.
这个函数的源码如下所示,主要用于设置滑动参数
[java] view plain copy
- /**
- *Startscrollingbyprovidingastartingpoint,thedistancetotravel,
- *andthedurationofthescroll.
- *
- *@paramstartXStartinghorizontalscrolloffsetinpixels.Positive
- *numberswillscrollthecontenttotheleft.
- *@paramstartYStartingverticalscrolloffsetinpixels.Positivenumbers
- *willscrollthecontentup.
- *@paramdxHorizontaldistancetotravel.Positivenumberswillscrollthe
- *contenttotheleft.
- *@paramdyVerticaldistancetotravel.Positivenumberswillscrollthe
- *contentup.
- *@paramdurationDurationofthescrollinmilliseconds.
- */
- publicvoidstartScroll(intstartX,intstartY,intdx,intdy,intduration){
- 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;
- }
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(); } }
更多相关文章
- Android 控件七 ImageView 控件
- Android SQLite数据库 《第一行代码》
- Android简明开发教程二十四:总结及示例代码下载
- 如何获取android源代码
- 自定义控件
- 代码讲解Android Scroller、VelocityTracker
- 基于ffmpeg的Android播放器开源代码