目录
1、View位置信息---left、translationX、x
2、滑动位置---MotionEvent和TouchSlop
3、滑动速度---VelocityTracker
4、手势检测---GestureDetector
5、View滑动方式1---ScrollTo/ScrollBy
6、View滑动方式2---动画
6.1、传统动画实现方式一
6.2、传统动画实现方式二
6.3、属性动画
7、View滑动方式3---改变布局参数
8、View滑动三种方式对比
9、弹性滑动---过渡动画
9.1、Scroller
9.2、动画
9.3、Handler#postDelayed
在Android 中View的位置由四个属性来决定: left、top、right、bottom。
注意:x、y轴的起始点为父容器的左上角。
从 Android 3.0 开始,View 增加了 x,y,translationX 和 translationY。
x,y 同样是 View 左上角相对父容器的坐标,但不同于 left 和 top ,这两个坐标点的值并一定都是相等的。而不相等的情况是由 translationX 和 translationY 值的设置引起的。
translationX 和 translationY 是 View 在做完动画移动后,左上角相对父容器左上角的偏移量,translationX 和 translationY 默认值都为 0。view动画移动后,x、left、translationX之间的关系如下:
x = left + translationX
如下图:红色框是一个LinearLayout,内部绿色框是一个ImageView,正常情况下ImageView是居中的,当我设置translationX值之后,它就会移动。
在Android中,我们可以通过OnTouchListener时间来监听 用户的触摸手机的事件:点击、滑动、长按等,同时它会返回一个MotionEvent的参数,通过MotionEvent可以获取我们点击的位置。
x和y返回的是相对于你点击的view左上角的x、y坐标;
rawX和rawY是相对于手机屏幕左上角的x、y坐标。
TouchSlop是系统所能识别出的被认为是滑动的最小距离,换句话说,如果你手指在屏幕上滑动的距离小于TouchSlop常量的话,系统认为你没有做滑动操作。
我们开发中,也可以通过这个常量来忽略掉一些 很小的滑动操作。
这个常量可以通过ViewConfiguration.get(this).scaledTouchSlop获取到。
VelocityTracker是用来进行速度追踪的,用于获取手指滑动屏幕的速度,它的实现方式如下:
需要注意的时候,在进行红色框中的速度获取之前,必须传入你要计算的速度的时间单位。也就是调用绿色框中computeCurrentVelocity(100)方法,参数单位是毫秒,这里我计算的是,100ms内滑动的速度。计算公式如下:
速度=(终点位置-起点位置)/100毫秒 结果=100毫秒滑过的像素数
当我们不使用的时候,应该进行重置并且回收内存。
注意:想要获取速度,监听的View,必须是可以滑动的View,比如RecyclerView、ScrollView。
GestureDetector是用来对用户操作行为进行检测和判断的。它的实现过程就是下图红色框的操作就可以了。
它的内部方法有6种,如下图蓝色框。
GestureDetector除了监听上面的6种事件以外,还能监听双击相关的操作。
它的实现只要在上面的detector对象上set一下双击的监听事件就可以了。
对于View的滑动我们可以通过ScrollTo/ScrollBy来实现,下面是它们的源码:
从上面的源码看到ScrollBy调用了ScrollTo。
ScrollBy和ScrollTo滑动的是内容的位置,不会改变该View的布局位置,从下面的“你好啊”就能知道,因为它的背景色没有移动,移动的只是“你好啊”。
ScrollBy实现的是基于当前位置的相对滑动,ScrollTo实现的是基于传递参数的绝对移动。
从下面的输出结果可以看出,我们view在滑动之前scrollX、Y都为0,当我们滑动后scrollX、Y值改变。
蓝色框:scrollTo--滑动的是绝对位置,多次触发以后,scrollX、Y值不变,也不会有滑动效果
绿色框:scrollBy---滑动的相对位置,多次触发以后,scrollX、Y值累加,view在一直滑动
Android中动画分为两大类:传统动画和属性动画, 传统动画又分为:帧动画和补间动画。
这里我们通过补间动画来实现View的滑动效果。
在anim资源文件夹中创建动画资源文件,然后在代码中调用即可。
第二种实现方式就是动态实现了。
注意1: 如果你想要动画移动后,保持移动后的位置,就需要设置fillAfter=true,否则在动画变化完之后,该View会恢复到动画之前的位置。
注意2:传统动画只改变的是该View的影像位置,它的真身其实没有变化,如果你对该View设置了点击的监听事件,你点击动画后的位置是没有反应的,你只有点击它的真身位置才行。
上面的scrollTo和scrollBy也只是改变了内容的位置,它的真身和背景色都不会变化。
针对这个问题,我们可以在可以创建一个和动画View一摸一样的View,同时具有相同的点击事件,当我们完成动画的一刹那,隐藏动画View,显示我们它的假身。
上面我们说了传统动画只能改变View的影像位置,它的真身其实还在原处,但是我们的属性动画能真正的移动我们View,也就是说你对View进行完属性动画后,你点击该View,它自身的点击事件会被触发, 但是传统动画,你点击它的动画后的位置,是没有反应的。
属性动画的实现如下图:
改变布局参数的移动效果,移动的也是View的真身。
scorllTo/scrollBy:实现简单,没有中间过渡动画效果;但是只有内容位置变化,其View本身的布局位置不会变化;
传统动画:实现简单,有中间过渡动画效果;但是只会改变影像位置,有交互的话,不建议使用;
属性动画:实现简单,有中间过渡动画效果;可以改变View的位置,可以用于有交互的View,但是只能在Android3.0以后使用;对于3.0一下的版本可以使用nineoldandroids这个库来弥补。
改变布局参数:实现稍微复杂,没有中间过渡动画效果;但是对于有交互的View来说,可以用该方法实现。
上面讲解了View滑动的三种方式,其中除了动画的方式带有中间的过渡滑动效果以外,其他两种方式都是没有过渡效果的,它们都是直接跳到了指定的位置。这种用户体验的方式很明显不是很好。
实现带有过渡效果的滑动动画的方式有三种:
1、使用Scroller;
2、使用动画
3、Handler#postDelayed+Thread#sleep
那么接下来,我们首先讲解怎样利用Scroller让scorllTo实现带有过渡动画的滑动效果。
要想让scorllTo实现带有过渡动画的滑动效果,首先需要重写View。
它的实现如下图:
我们来看一下Scoller的startScroll()的源码,我们会发现这里没有做任何的开始动画的操作,只有赋值操作。
那么哪里实现了过渡动画呢?
答案在startScroll()下面的invalidate()方法,当我们重新绘制该View的时候,会触发绿色框内的computeScroll()方法,然后该方法会触发mScroller.computeScrollOffset(),该方法内部会根据时间计算出你当前View要滑动的距离,然后在重新调用postInvalidate()方法,重新绘制该View,然后再重新触发computeScroll()方法。
这样我们的view会在1000毫秒内,不断地重新绘制我们的View,这样就实现了过渡动画的效果。
对于动画,是自带过渡效果的,所以我们可以直接使用就行。
但是对于scrollTo,我们可以利用属性动画来实现相同的效果。
下面除了可以使用Handler#postDelayed实现过渡动画以外,还可以使用View#postDelayed方法,或者使用Thread#sleep实现,实现的思路都一样。