在编写自定义滑动控件时常常会用到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事件中获取速度,调用流程可如下列所示:

  1. VelocityTracker vTracker = null;
  2. @Override
  3. public boolean onTouchEvent(MotionEvent event){
  4. int action = event.getAction();
  5. switch(action){
  6. case MotionEvent.ACTION_DOWN:
  7. if(vTracker == null){
  8. vTracker = VelocityTracker.obtain();
  9. }else{
  10. vTracker.clear();
  11. }
  12. vTracker.addMovement(event);
  13. break;
  14. case MotionEvent.ACTION_MOVE:
  15. vTracker.addMovement(event);
  16. //设置单位,1000 表示每秒多少像素(pix/second),1代表每微秒多少像素(pix/millisecond)。
  17. vTracker.computeCurrentVelocity(1000);
  18. //从左向右划返回正数,从右向左划返回负数
  19. System.out.println("the x velocity is "+vTracker.getXVelocity());
  20. //从上往下划返回正数,从下往上划返回负数
  21. System.out.println("the y velocity is "+vTracker.getYVelocity());
  22. break;
  23. case MotionEvent.ACTION_UP:
  24. case MotionEvent.ACTION_CANCEL:
  25. vTracker.recycle();
  26. break;
  27. }
  28. return true;
  29. }

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

  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. */
  7. public void computeScroll()
  8. {
  9. }

parentView在绘制式,会调用dispatchDraw(Canvas canvas),该函数会调用ViewGroup中的每个子view的boolean draw(Canvas canvas, ViewGroup parent, long drawingTime),用户绘制View,此函数在绘制View的过程中会调用computeScroll()
下面给出一段代码:

  1. @Override
  2. public void computeScroll() {
  3. // TODO Auto-generated method stub
  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, "### getleft is " + getLeft() + " ### getRight is " + getRight());
  9. postInvalidate();
  10. }
  11. else
  12. Log.i(TAG, "have done the scoller -----");
  13. }

这段代码在滑动view之前先调用mScroller.computeScrollOffset()来判断滑动动画是否已结束。computerScrollerOffset()的源代码如下:

  1. /**
  2. * Call this when you want to know the new location.  If it returns true,
  3. * the animation is not yet finished.
  4. */
  5. public boolean computeScrollOffset() {
  6. if (mFinished) {
  7. return false;
  8. }
  9. //滑动已经持续的时间
  10. int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
  11. //若在规定时间还未用完,则继续设置新的滑动位置mCurrX和mCurry
  12. if (timePassed < mDuration) {
  13. switch (mMode) {
  14. case SCROLL_MODE:
  15. float x = 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. case FLING_MODE:
  24. final float t = (float) timePassed / mDuration;
  25. final int index = (int) (NB_SAMPLES * t);
  26. float distanceCoef = 1.f;
  27. float velocityCoef = 0.f;
  28. if (index < NB_SAMPLES) {
  29. final float t_inf = (float) index / NB_SAMPLES;
  30. final float t_sup = (float) (index + 1) / NB_SAMPLES;
  31. final float d_inf = SPLINE_POSITION[index];
  32. final float d_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. // Pin to mMinX <= mCurrX <= mMaxX
  39. mCurrX = Math.min(mCurrX, mMaxX);
  40. mCurrX = Math.max(mCurrX, mMinX);
  41. mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
  42. // Pin to mMinY <= 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. return true;
  57. }

ViewGroup.computeScroll()被调用时机:
当我们执行ontouch或invalidate()或postInvalidate()都会导致这个方法的执行。

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

  1. public void moveToRightSide(){
  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.
这个函数的源码如下所示,主要用于设置滑动参数

  1. /**
  2. * Start scrolling by providing a starting point, the distance to travel,
  3. * and the duration of the scroll.
  4. *
  5. * @param startX Starting horizontal scroll offset in pixels. Positive
  6. *        numbers will scroll the content to the left.
  7. * @param startY Starting vertical scroll offset in pixels. Positive numbers
  8. *        will scroll the content up.
  9. * @param dx Horizontal distance to travel. Positive numbers will scroll the
  10. *        content to the left.
  11. * @param dy Vertical distance to travel. Positive numbers will scroll the
  12. *        content up.
  13. * @param duration Duration of the scroll in milliseconds.
  14. */
  15. public void startScroll(int startX, int startY, int dx, int dy, int duration) {
  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才停下来。

附上完整的实例代码:

自定义Android可滑动控件源码

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

代码讲解Android Scroller、VelocityTracker的更多相关文章

  1. 代码解说Android Scroller、VelocityTracker

    在编写自己定义滑动控件时经常会用到Android触摸机制和Scroller及VelocityTracker.Android Touch系统简单介绍(二):实例具体解释onInterceptTouchE ...

  2. Android Scroller解析

    作用 这个类封装了滚动操作,如帮我们处理手指抬起来时候的滑动操作.与ViewGroup的scrollTo(),scrollBy()的生硬式移动,Scroller提供了一个更加柔和的移动效果.Scrol ...

  3. 深入讲解Android Property机制

    深入讲解Android Property机制 侯亮 1      概述 Android系统(本文以Android 4.4为准)的属性(Property)机制有点儿类似Windows系统的注册表,其中的 ...

  4. 第二章--Win32程序运行原理 (部分概念及代码讲解)

    学习<Windows程序设计>记录 概念贴士: 1. 每个进程都有赋予它自己的私有地址空间.当进程内的线程运行时,该线程仅仅能够访问属于它的进程的内存,而属于其他进程的内存被屏蔽了起来,不 ...

  5. 一行代码解决Android M新的运行时权限问题

    Android M运行时权限是个啥东西 啥是运行时权限呢?Android M对权限管理系统进行了改版,之前我们的App需要权限,只需在manifest中申明即可,用户安装后,一切申明的权限都可来去自如 ...

  6. 用kotlin方式打开《第一行代码:Android》

    参考:<第一行代码:Android>第2版--郭霖 注1:本文为原创,例子可参考郭前辈著作:<第一行代码:Android> 注2:本文不赘述android开发的基本理论,不介绍 ...

  7. 用kotlin方式打开《第一行代码:Android》之开发酷欧天气(1)

    参考:<第一行代码:Android>第2版--郭霖 注1:本文为原创,例子可参考郭前辈著作:<第一行代码:Android>第2版 注2:本文不赘述android开发的基本理论, ...

  8. 『HTML5实现人工智能』小游戏《井字棋》发布,据说IQ上200才能赢【算法&代码讲解+资源打包下载】

    一,什么是TicTacToe(井字棋) 本游戏为在下用lufylegend开发的第二款小游戏.此游戏是大家想必大家小时候都玩过,因为玩它很简单,只需要一张草稿纸和一只笔就能开始游戏,所以广受儿童欢迎. ...

  9. 第四章:重构代码[学习Android Studio汉化教程]

    第四章 Refactoring Code The solutions you develop in Android Studio will not always follow a straight p ...

随机推荐

  1. windows编程中 一些前缀区分 IDR和IDD

    IDC_:控件的ID命名前缀(Control) IDM_:菜单的ID命名前缀(Menu) IDD_:对话框的ID命名前缀(Dialog) IDR_:资源的ID命名前缀(Resource) IDS_:字 ...

  2. amf0解释一下

    就简单记录一下省了以后忘了,amf0其实就几种数据格式的网络传输格式,比如数字,字符串,这些格式在传输的时候他给单独序列化了一下,主要支持以下这些: #define AMF0_NUMBER ((uin ...

  3. 关于call 与 apply 那些事

    1.定义: call : 调用一个对象的一个方法,以另一个对象替换当前的对象. apply : 应用某一对象的一个方法,用另一个对象替换当前的对象. 2.用法: call:call(thisObj, ...

  4. 浅谈Chrome V8引擎中的垃圾回收机制

    垃圾回收器 JavaScript的垃圾回收器 JavaScript使用垃圾回收机制来自动管理内存.垃圾回收是一把双刃剑,其好处是可以大幅简化程序的内存管理代码,降低程序员的负担,减少因 长时间运转而带 ...

  5. php基础知识【函数】(8)xml和变量函数

     一.XML函数 参数类型 data    --string,需要解析的数据集. parser  --resource,一个指向要取得字节索引的 XML 解析器的引用.  1.创建和释放XMl解析器 ...

  6. 知识库系统/知识管理系统 WCP

    知识库系统/知识管理系统 WCP 本项目的应用场景是管理技术团队的相关知识(API.代码片段.知识定义.技术经验...) 但是其应用并不局限于这些应用,当然你最好下载一个安装版先试一试.其实这就是一个 ...

  7. 在 iOS 应用中直接跳转到 AppStore 的方法--备用

    找到应用程序的描述链接,比如:http://itunes.apple.com/gb/app/yi-dong-cai-bian/id391945719?mt=8 然后将 http:// 替换为 itms ...

  8. 【Java】对服务器程序的理解

    Login:------------->方法 Data:----->类.API数据 Collection:-------->集合 Data Source File: Database ...

  9. JavaScript的应用

    DOM, BOM, XMLHttpRequest, Framework, Tool (Functionality) Performance (Caching, Combine, Minify, JSL ...

  10. SharePoint ListTemplateType enumeration

    from microsoft http://msdn.microsoft.com/en-us/library/office/microsoft.sharepoint.client.listtempla ...