在编写自定义滑动控件时常常会用到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. ExtJs中动态加载机制研究(转)

    觉得写的太好了,怕弄丢了,转一下:http://extjs.org.cn/node/659 昨天我们team对于extjs的动态加载机制做了些深入研究,这里先share下controller加载的结果 ...

  2. js常用效果

    //创建元素 var txt1="<p style='color:red'>我是由HTML创建的</p>"; // 以 HTML 创建新元素 var txt ...

  3. 入门5:PHP 语法基础——流程控制

    一.if...else 语句 if( ) else{ } 如果 .... 就.... 否则.... if(判断){ 判断成立 则执行该表达式 }else{ 如果上方判断都不成立 则执行该表达式 } i ...

  4. memcache 操作类

    <?php /** * memcache 操作实现 * @author timeless */ class Memcache_manage { //CI原始的信息 private $_ci; p ...

  5. KVO/KVC总结

    KVO/KVC总结       下面是根据网上文章的总结,方便查看. 在网上看别人的文章,了解KVC.KVO,有个kvo-kvc的例子,就是改变数组的内容(插入和删除),同步改变tableview中的 ...

  6. 【Linux】 诊断工具-strace

    1,别人家的总结: http://www.cnblogs.com/bangerlee/archive/2012/02/20/2356818.html 2,我自己碰到问题: 暂无. 3,使用场景: 程序 ...

  7. 【Java】Java Socket编程(1)基本的术语和概念

    计算机程序能够相互联网,相互通讯,这使一切都成为可能,这也是当今互联网存在的基础.那么程序是如何通过网络相互通信的呢?这就是我记录这系列的笔记的原因.Java语言从一开始就是为了互联网而设计的,它为实 ...

  8. Tiling(递推+大数)

    Description In how many ways can you tile a 2xn rectangle by 2x1 or 2x2 tiles? Here is a sample tili ...

  9. 数据结构(trie,启发式合并):HDU 5841 Alice and Bob

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAABJEAAAE6CAIAAAApz1RvAAAgAElEQVR4nO3d3css1b3g8fyTdbHJbD

  10. 算法优化(动态规划):COGS 2009. [USACO Mar09]餐厅清扫

    2009. [USACO Mar09]餐厅清扫 ★★☆   输入文件:cleanup.in   输出文件:cleanup.out   简单对比时间限制:1 s   内存限制:256 MB [题目描述] ...