博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
代码解说Android Scroller、VelocityTracker
阅读量:6501 次
发布时间:2019-06-24

本文共 6014 字,大约阅读时间需要 20 分钟。

在编写自己定义滑动控件时经常会用到Android触摸机制和Scroller及VelocityTracker。对Android触摸机制须要用到的函数进行了具体的解释。本文主要介绍两个重要的类:Scroller及VelocityTracker。利用上述知识,最后给出了一个自己定义滑动控件的demo,该demo类似于。

ImageGallery通常是用GridView来实现的,能够左右滑动。本样例实现的控件直接继承一个ViewGroup,对其回调函数如 onTouchEvent、onInterceptTouchEvent、computeScroll等进行重载。弄懂该代码。对Android touch的认识将会更深一层。

VelocityTracker:用于对触摸点的速度跟踪,方便获取触摸点的速度。

使用方法:一般在onTouchEvent事件中被调用。先在down事件中获取一个VecolityTracker对象,然后在move或up事件中获取速度,调用流程可例如以下列所看到的:

VelocityTracker vTracker = null;@Override  public boolean onTouchEvent(MotionEvent event){  	int action = event.getAction();  	switch(action){  	case MotionEvent.ACTION_DOWN:  		if(vTracker == null){  			vTracker = VelocityTracker.obtain();  		}else{  			vTracker.clear();  		}  		vTracker.addMovement(event);  		break;  	case MotionEvent.ACTION_MOVE:  		vTracker.addMovement(event);  		//设置单位,1000 表示每秒多少像素(pix/second),1代表每微秒多少像素(pix/millisecond)。 		vTracker.computeCurrentVelocity(1000);  		//从左向右划返回正数,从右向左划返回负数		System.out.println("the x velocity is "+vTracker.getXVelocity());  		//从上往下划返回正数,从下往上划返回负数		System.out.println("the y velocity is "+vTracker.getYVelocity());  		break;  	case MotionEvent.ACTION_UP:  	case MotionEvent.ACTION_CANCEL:  		vTracker.recycle();  		break;  	}  	return true;  }

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

/** * Called by a parent to request that a child update its values for mScrollX * and mScrollY if necessary. This will typically be done if the child is * animating a scroll using a {@link android.widget.Scroller Scroller} * object. */public void computeScroll(){}
parentView在绘制式。会调用dispatchDraw(Canvas canvas),该函数会调用ViewGroup中的每一个子view的boolean draw(Canvas canvas, ViewGroup parent, long drawingTime),用户绘制View,此函数在绘制View的过程中会调用computeScroll()
以下给出一段代码:
@Overridepublic void computeScroll() {		// TODO Auto-generated method stub	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, "### getleft is " + getLeft() + " ### getRight is " + getRight());		postInvalidate();	}	else		Log.i(TAG, "have done the scoller -----");}
这段代码在滑动view之前先调用mScroller.computeScrollOffset()来推断滑动动画是否已结束。computerScrollerOffset()的源码例如以下:

/** * Call this when you want to know the new location.  If it returns true, * the animation is not yet finished. */ public boolean computeScrollOffset() {	if (mFinished) {		return false;	}		//滑动已经持续的时间	int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);	//若在规定时间还未用完,则继续设置新的滑动位置mCurrX和mCurry	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;}
ViewGroup.computeScroll()被调用时机:
当我们运行ontouch或invalidate()或postInvalidate()都会导致这种方法的运行。

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

public void moveToRightSide(){	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();}
上述代码用到了一个函数:void android.widget.Scroller.startScroll(int startX, int startY, int dx, int dy, int duration)
当startScroll运行过程中即在duration时间内,computeScrollOffset  方法会一直返回true,但当动画运行完毕后会返回返加false.
这个函数的源代码例如以下所看到的,主要用于设置滑动參数

/** * Start scrolling by providing a starting point, the distance to travel, * and the duration of the scroll. *  * @param startX Starting horizontal scroll offset in pixels. Positive *        numbers will scroll the content to the left. * @param startY Starting vertical scroll offset in pixels. Positive numbers *        will scroll the content up. * @param dx Horizontal distance to travel. Positive numbers will scroll the *        content to the left. * @param dy Vertical distance to travel. Positive numbers will scroll the *        content up. * @param duration Duration of the scroll in milliseconds. */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;}
invalidate()会使得视图重绘,导致parent调用了dispatchDraw(Canvas canvas),然后递归调用child View的draw()函数。该函数又会调用我们定义的computeScroll(), 而这个函数又会调用mScroller.computeScrollOffset()推断动画是否结束。若没结束则继续重绘直到直到startScroll中设置的时间耗尽mScroller.computeScrollOffset()返回false才停下来。
附上完整的实例代码:

执行效果图例如以下,滑动屏幕会显示不同的图片。

你可能感兴趣的文章
PHP通过读取DOM抓取信息
查看>>
DICOM医学图像处理:DICOM网络传输
查看>>
nio和传统Io的区别
查看>>
移动端网页布局中需要注意事项以及解决方法总结
查看>>
(原创)Linux下查看系统版本号信息的方法
查看>>
oracle
查看>>
redis使用过程中主机内核层面的一些优化
查看>>
我也要谈谈大型网站架构之系列(2)——纵观历史演变(下)
查看>>
大话设计模式(Golang) 二、策略模式
查看>>
使用PostgreSQL 9.6 架设mediawiki服务器
查看>>
数据库服务器硬件对性能的影响
查看>>
LVM
查看>>
windows+群辉服务器环境下,搭建git版本管理
查看>>
Boolean类型
查看>>
Ubuntu 修改源
查看>>
php 几个比较实用的函数
查看>>
(译)OpenGL ES2.0 – Iphone开发指引
查看>>
@RestController 与 @RequestMapping
查看>>
黑马程序员.bobo.DAY.1
查看>>
Unity shader 官网文档全方位学习(二)
查看>>