前言
在平时打开图库经常能看到这个效果 闲暇时光决定 模仿了一下写了这个demo, 下边就是效果图。这个效果主要利用GestureDetectorCompat处理手势 然后通过OverScroller实现。
正文大纲
GestureDetectorCompat使用
放大以及平移功能的实现
-
放大功能实现
-
平移功能实现
-
平滑过程的实现
OverScroller实现快速滑动效果
-
OverScroller及其滑动范围
-
post和postOnAnimation区别
-
OverScroller和Scroller区别
总结
正文
GestureDetectorCompat使用
首先看初始化创建对象
1. `detector = newGestureDetectorCompat(context, this);`
然后我们利用GestureDetectorCompat 拦截当前的的事件
1. `@Override`
2. `publicboolean onTouchEvent(MotionEventevent) {`
3. `return detector.onTouchEvent(event);`
4. `}`
处理好这些,GestureDetectorCompat可实现的回调就可以响应了。
1. `@Override`
2. `publicboolean onDown(MotionEvent e) {`
3. `returntrue;`
4. `}`
6. `@Override`
7. `publicvoid onShowPress(MotionEvent e) {`
9. `}`
10. `@Override`
11. `publicboolean onSingleTapUp(MotionEvent e) {`
12. `returnfalse;`
13. `}`
14. `@Override`
15. `publicboolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {`
17. `}`
18. `@Override`
19. `publicvoid onLongPress(MotionEvent e) {`
21. `}`
22. `@Override`
23. `publicboolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {`
24. `returnfalse;`
25. `}`
28. `@Override`
29. `publicboolean onDoubleTap(MotionEvent e) {`
31. `}`
这时候我们会有两类疑问,这些相似的响应的方法是什么?他们和我们自带的那些onclick有什么区别?下边简单介绍一下
1. `publicboolean onDown(MotionEvent e);`
onDown是我们收到ACTION_DOWN时候就会被调用,一般来说,通过该事件处理我当前事件是否被消费了,然后返回的值通过detector.onTouchEvent(event)返回体现出来。
1. `publicvoid onShowPress(MotionEvent e);`
onShowPress是一个预按下的事件,在GestureDetector中是响应到ACTION_DOWN事件,100毫秒之后触发的。
1. `publicboolean onSingleTapUp(MotionEvent e);`
onSingleTapUp很好理解 每次点击抬起时调用。这里要注意,如果长按事件被执行那么onSingleTapUp就不执行。
1. `publicboolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);`
onScroll 类似ACTION_MOVE手指发生移动之后调用,e1表示按下时候的时间,e2表示正在处理的事件,distanceX和distanceY表示移动距离。
1. `publicvoid onLongPress(MotionEvent e);`
onLongPress是长按时调用的默认事件是600毫秒。可以通过detector.setIsLongpressEnabled(false);将长按事件取消。
1. `publicboolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);`
onFling是手指快速滑动时调用的,下边我会介绍。
1. `publicboolean onDoubleTap(MotionEvent e);`
2. `publicboolean onDoubleTapEvent(MotionEvent e);`
onDoubleTap和onDoubleTapEvent都是双击点击事件,区别在于onDoubleTapEvent第二次点击可以滑动。
放大以及平移功能的实现
放大功能实现
1. `@Override`
2. `public boolean onDoubleTap(MotionEvent e) {`
3. `isEnlarge = !isEnlarge;`
4. `scaleAnimator = ObjectAnimator.ofFloat(this, "currentScale", 0);`
5. `if (isEnlarge) {`
6. `scaleAnimator.start();`
7. `} else {`
8. `scaleAnimator.reverse();`
9. `}`
10. `....`
11. `}`
13. `@Override`
14. `protected void onDraw(Canvas canvas) {`
15. `super.onDraw(canvas);`
16. `....`
17. `canvas.scale(currentScale, currentScale, getWidth() / 2f, getHeight() / 2f);`
18. `}`
onDoubleTap 我利用onDoubleTap双击事件 控制isEnlarge操作动画来实现图片的放大和缩小的效果。
接下来我们去看一下放到后如何进行移动的。
平移功能实现
1. `@Override`
2. `protected void onDraw(Canvas canvas) {`
3. `super.onDraw(canvas);`
4. `....`
5. `canvas.translate(motionEventX * currentScale, motionEventY * currentScale);`
6. `....`
7. `}`
这个也很简单,通过canvas.translate 改变坐标系,然后随着手指对应图片的滑动的。那么motionEventX和motionEventY是如何计算出来的呢?这里 我们是通过GestureDetectorCompat中的onScroll这个方法计算出来的,为什么会有两个MotionEvent参数,下边简单叙述一下对应的参数的作用。
平滑过程的实现
1. `publicboolean onScroll(MotionEvent down, MotionEventevent, float distanceX, float distanceY);`
MotionEvent down 代表按下的事件,MotionEvent event代表当前的事件,distanceX代表上一个点到当前点的x轴距离,distanceY代表上一个点到当前点的Y轴距离。
1. `@Override`
2. `public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {`
3. `if (isEnlarge) {`
4. `motionEventX -= distanceX;`
5. `motionEventY -= distanceY;`
6. `....`
7. `invalidate();`
8. `}`
9. `return false;`
10. `}`
上边是onScroll的具体实现,motionEventX和motionEventY是通过distanceX和distanceY计算出来的,但是这里为什么要用减法呢?手指向右移动图片,distanceX是正值,canvas.translate的坐标也要是负值图片才能要向右移动。同理distanceY也是如此。
这里要注意motionEventX和motionEventY的不要超过屏幕的边距。
OverScroller实现快速滑动效果
OverScroller及其滑动范围
运行了之后,我发现快滑的时候没有效果,这是因为我们没有对onFling进行处理,我们可以利用OverScroller中的scroller.fling方法进行计算,处理快速滑动的事件。
1. `private OverScroller scroller;`
3. `@Override`
4. `public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {`
5. `if (isEnlarge) {`
6. `scroller.fling((int) motionEventX, (int) motionEventY, (int) velocityX, (int) velocityY,`
7. `-(int) (bitmap.getWidth() * bigScale - getWidth()) / 2,`
8. `(int) (bitmap.getWidth() * bigScale - getWidth()) / 2,`
9. `-(int) (bitmap.getHeight() * bigScale - getHeight()) / 2,`
10. `(int) (bitmap.getHeight() * bigScale - getHeight()) / 2);`
11. `postOnAnimation(motionRunnable);`
12. `}`
13. `return false;`
14. `}`
scroller.fling的参数有点多 但是很好记。先看下八个参数是什么。
1. `publicvoid fling(int startX, int startY, int velocityX, int velocityY,`
2. `int minX, int maxX, int minY, int maxY);`
startX和startY代表初始的位置,在这个demo中就是我们手指点击的位置motionEventX,和motionEventY。velocityX和velocityY代表速度,手指抬起的时和之前的位移除以时间的计算,得出的结果。minX maxX minY maxY处理边距,这里以中心点为原点,放大后超过图片大小的部分除以2找到对应边界,如下图所示。
手指可滑动的范围是黑色框以外的部分。
上边说到scroller.fling是对于滑动方法的计算,但是我们需要对当前布局进行刷新。这里就用到了一个方法scroller.computeScrollOffset()判断是否完成滚动从而进行刷新。这里可以看一下postOnAnimation(motionRunnable);方法。
1. `MotionRunner motionRunnable = new MotionRunner();`
2. `....`
3. `postOnAnimation(motionRunnable);`
1. `class MotionRunner implements Runnable {`
3. `@Override`
4. `public void run() {`
5. `if (scroller.computeScrollOffset()) {`
6. `motionEventX = scroller.getCurrX();`
7. `motionEventY = scroller.getCurrY();`
8. `invalidate();`
9. `postOnAnimation(this);`
10. `}`
11. `}`
12. `}`
这样我们每一帧动画执行完成后,不断调用postOnAnimation,直到scroller滚动完成。
post和postOnAnimation区别
这里postOnAnimation和post区别在于postOnAnimation在下一帧之后执行而post是立即在主线程中执行,有可能post执行要比postOnAnimation快,或者一帧中post会执行多次。这里使用ViewCompat.postOnAnimation兼容性更好。
OverScroller和Scroller区别
在这个demo中我用的是OverScroller,但是他与Scroller有什么区别呢?简单说下overscrller会多两个参数
1. `publicvoid fling(int startX, int startY, int velocityX, int velocityY,`
2. `int minX, int maxX, int minY, int maxY, int overX, int overY);`
overX和overY代表超过范围的位置,在原来基础的距离上增加overx和overy。
总结
这里我们利用GestureDetectorCompat处理手势操作,处理双击事件以及滑动事件。双击事件执行后通过动画和canvas.scale()实现了放大缩小操作,通过滑动事件(onFling和onScroll)来处理图片放大后滑动。
网友评论