当前位置:首页>编程日记>正文

代码解说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事件中获取速度,调用流程可例如以下列所看到的:

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()
以下给出一段代码:
@Override
public void computeScroll() {	// TODO Auto-generated method stubLog.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();}elseLog.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和mCurryif (timePassed < mDuration) {switch (mMode) {case SCROLL_MODE:float x = timePassed * mDurationReciprocal;if (mInterpolator == null)x = viscousFluid(x); elsex = 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 <= mMaxXmCurrX = Math.min(mCurrX, mMaxX);mCurrX = Math.max(mCurrX, mMinX);mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));// Pin to mMinY <= mCurrY <= mMaxYmCurrY = 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才停下来。

附上完整的实例代码:

自己定义Android可滑动控件源代码

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

代码解说Android Scroller、VelocityTracker 配图01代码解说Android Scroller、VelocityTracker 配图02

http://www.coolblog.cn/news/7c25fc15444cbff5.html

相关文章:

  • asp多表查询并显示_SpringBoot系列(五):SpringBoot整合Mybatis实现多表关联查询
  • s7day2学习记录
  • 【求锤得锤的故事】Redis锁从面试连环炮聊到神仙打架。
  • 矿Spring入门Demo
  • 拼音怎么写_老师:不会写的字用圈代替,看到孩子试卷,网友:人才
  • Linux 实时流量监测(iptraf中文图解)
  • Win10 + Python + GPU版MXNet + VS2015 + RTools + R配置
  • 美颜
  • shell访问php文件夹,Shell获取某目录下所有文件夹的名称
  • 如何优雅的实现 Spring Boot 接口参数加密解密?
  • LeCun亲授的深度学习入门课:从飞行器的发明到卷积神经网络
  • Mac原生Terminal快速登录ssh
  • java受保护的数据与_Javascript类定义语法,私有成员、受保护成员、静态成员等介绍...
  • mysql commit 机制_1024MySQL事物提交机制
  • 支撑微博千亿调用的轻量级RPC框架:Motan
  • jquery 使用小技巧
  • 2019-9
  • 法拉利虚拟学院2010 服务器,法拉利虚拟学院2010
  • vscode pylint 错误_将实际未错误的py库添加到pylint白名单
  • 科学计算工具NumPy(3):ndarray的元素处理
  • 工程师在工作电脑存 64G 不雅文件,被公司开除后索赔 41 万,结果…
  • linux批量创建用户和密码
  • newinsets用法java_Java XYPlot.setInsets方法代碼示例
  • js常用阻止冒泡事件
  • 气泡图在开源监控工具中的应用效果
  • 各类型土地利用图例_划重点!国土空间总体规划——土地利用
  • php 启动服务器监听
  • dubbo简单示例
  • 【设计模式】 模式PK:策略模式VS状态模式
  • [iptables]Redhat 7.2下使用iptables实现NAT
  • Ubuntu13.10:[3]如何开启SSH SERVER服务
  • CSS小技巧——CSS滚动条美化
  • JS实现-页面数据无限加载
  • 阿里巴巴分布式服务框架 Dubbo
  • 最新DOS大全
  • Django View(视图系统)
  • 阿里大鱼.net core 发送短信
  • 程序员入错行怎么办?
  • 两张超级大表join优化
  • 第九天函数
  • Linux软件安装-----apache安装
  • HDU 5988 最小费用流
  • Sorenson Capital:值得投资的 5 种 AI 技术
  • 《看透springmvc源码分析与实践》读书笔记一
  • 正式开课!如何学习相机模型与标定?(单目+双目+鱼眼+深度相机)
  • Arm芯片的新革命在缓缓上演
  • nagios自写插件—check_file
  • python3 错误 Max retries exceeded with url 解决方法
  • 行为模式之Template Method模式
  • 通过Spark进行ALS离线和Stream实时推荐