在编写自定义滑动控件时常常会用到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. Ubuntu Apache 伪静态配置 url重写 步骤

    1.加载rewrite模块sudo ln -s /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/rewrite.l ...

  2. 根据select不同的选项实现相应input框添加项的显示

    实现效果: @1.单击包时,显示包时的添加项 @2.单击包里程,显示包里程的添加项 二  代码实现: 给select添加change事件 获取当前select的value 根据value判断对象显示其 ...

  3. C#【数据库】 Excel打开到DataGridView

    if (openFileDialog1.ShowDialog() == DialogResult.OK) { Filename = openFileDialog1.FileName; string s ...

  4. 如何正确的使用uwsgi

    简单的安装过程可以在这里找到,这里主要说一下如何配置uwsgi的服务,将uwsgi服务加入系统进程,你可以使用如下两种方式安装 apt-get apt-get install uwsgi 该命令会自动 ...

  5. .NET 元数据

    1. 安装 ILDASM 工具 VS -- 外部工具 -- 添加 -- 命令行为:C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NET ...

  6. core dump

    Core Dump?! 整理:Wilbur Lang 何谓 core? 在使用半导体作为内存的材料前,人类是利用线圈当作内存的材料(发明 者为王安),线圈就叫作 core ,用线圈做的内存就叫作 co ...

  7. Unity3d webplayer获取url参数

    Unity3d webplayer获取url参数 1.Unity中代码,在Start方法中调用了HTML的GetUrl方法!Application.ExternalCall( "SayHel ...

  8. AOP举例子

    切面类TestAspect package com.spring.aop; /** * 切面 * */ public class TestAspect { public void doAfter(Jo ...

  9. A Simple problem

    http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2497 题意:给出顶点数,边数及节点s,判断s是 ...

  10. 平衡树(Splay):Splaytree POJ 3580 SuperMemo

    SuperMemo         Description Your friend, Jackson is invited to a TV show called SuperMemo in which ...