在编写自定义滑动控件时常常会用到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. js对象的复制,传递,新增,删除和比较

    当我们把一个某个对象拷贝或者传递给某个函数时,往往传递的是该对象的引用. 因此我们在引用上做的任何改动,都将会影响到它所引用的原对象.  复制,拷贝  var o = { add: 'Changdao ...

  2. java中如何操作数据库(增删改查)

    EntityManager 是用来对实体Bean 进行操作的辅助类.他可以用来产生/删除持久化的实体Bean,通过主键查找实体bean,也可以通过EJB3 QL 语言查找满足条件的实体Bean.实体B ...

  3. Java常量和变量

    1.Java运行原理 编译+解释型语言: 程序代码经编译后转换为一种称为java字节码(.class文件)的中间语言 file.java--->Class.class java虚拟机JVM将字节 ...

  4. how to make choices about girls?

    yb ldge nvhl, yige hf csmn, hv bmig, hv zofj, hfyb uhjnxn;ldyige ybdm bfbfde, gjjt hf djip, djui hfk ...

  5. KVO初探

    一,概述 KVO,即:Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知.简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应 ...

  6. Linq/EF/lambda Group by/Order by 多个字段详细用法

    1)单个字段Group by: //a.Key类型与a.Province字段类型一样  .GroupBy(a => a.Province).Select(a => a.Key).ToLis ...

  7. 提高Order by语句查询效率的两个思路

    提高Order by语句查询效率的两个思路 2011-03-01 13:07 水太深 ITPUB 字号:T | T 在MySQL数据库中,Order by语句的使用频率是比较高的.但是众所周知,在使用 ...

  8. codevs 4163 hzwer与逆序对

    传送门 题目描述 Description hzwer在研究逆序对. 对于数列{a},如果有序数对(I,j)满足:i<j,a[i]>a[j],则(i,j)是一对逆序对. 给定一个数列{a}, ...

  9. Google测试精华文章(1) - 测试行为,而非实现

    Your trusty Calculator class is one of your most popular open source projects, with many happy users ...

  10. Dao 处理

    1. 写一个基础的接口和类来做基本的操作 /** * */ package com.wolfgang.dao; import java.util.List; /** * @author Adminis ...