在编写自定义滑动控件时常常会用到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. Mobile开发之meta篇

    Mobile开发之meta篇 <meta name="viewport" content="width=device-width, initial-scale=1, ...

  2. 本大神教你用PHP把文本内容转换成16进制数字,进行加密

    <?php $a="杨波"; $b = bin2hex($a); echo $a."<br />"; $c = pack("H*&q ...

  3. 重构oceanbase的一个函数

    我去,今天读了一下ob的源码,感觉有点乱啊!!!好吧,当作练手,我重构了一个函数 void* ObMySQLCallback::decode(easy_message_t* m) { uint32_t ...

  4. 3ds max 2016 新功能

    3ds max 2016 新功能_MCG节点编辑器 这是介绍视频下载: http://pan.baidu.com/s/1gds4wqJ

  5. JavaScript DES 加密

    最近做网页数据加密工作, 使用CryptoJS v3.1.2 这个JavaScript脚本,网上比较有质量的文章实在太少,经验证加密结果与Asp.net DES加密结果一致 参考文章 https:// ...

  6. java 内存 垃圾回收调优

    要了解Java垃圾收集机制,先理解JVM内存模式是非常重要的.今天我们将会了解JVM内存的各个部分.如何监控以及垃圾收集调优. Java(JVM)内存模型 正如你从上面的图片看到的,JVM内存被分成多 ...

  7. 【最大流】ECNA 2015 F Transportation Delegation (Codeforces GYM 100825)

    题目链接: http://codeforces.com/gym/100825 题目大意: N(N<=600)个点,每个点有个名字Si,R(R<=200)个生产商在R个点上,F(F<= ...

  8. HDU-1207 汉诺塔II

    汉诺塔  四根所需要的步数的规律: 规律:a[1]=1;a[2]=a[1]+2;a[3]=a[2]+2;(2个加2^1)a[4]=a[3]+4;a[5]=a[4]+4;a[6]=a[5]+4;(3个加 ...

  9. HDOJ(HDU) 2309 ICPC Score Totalizer Software(求平均值)

    Problem Description The International Clown and Pierrot Competition (ICPC), is one of the most disti ...

  10. HDOJ(HDU) 2138 How many prime numbers(素数-快速筛选没用上、)

    Problem Description Give you a lot of positive integers, just to find out how many prime numbers the ...