Android手势ImageView三部曲 第一部
这几天一直在研究github上的photoview跟gestureimageview,发现写的都很牛,看了很久的代码,于是打算把自己所看的一些东西总结一下,内容还是很多的,但是很有含金量哈~~
先附上两个开源项目的链接:
gestureimageview:
photoview:https://github.com/chrisbanes/photoview
这样说有点乏味哈,先看看我们今天要实现的效果:
当一个手指按住图片的时候,此时的效果为拖拽的效果。
当两个手指按住图片的时候,手指旋转则图片跟着旋转,手指缩放则图片缩放。
效果图大致为(我模拟器不太好模拟旋转):
好了下面我们来实现一下这个手势缩放imageview:
首先我们创建一个叫matriximageview的类去继承imageview,然后重写其构造方法(我就不考虑直接new的情况了哈):
public class matriximageview2 extends imageview { public matriximageview2(context context, attributeset attrs) { super(context, attrs); initview(); } }
然后我们需要定义几种当前view的状态:
mode_none(初始状态);
mode_drag(拖拽状态);
mode_zoom(两个手指缩放状态)
public class matriximageview2 extends imageview { private static final int mode_none = 190; private static final int mode_drag = 468; private static final int mode_zoom = 685; ..... }
我们对imageview做旋转、缩放、位移等操作主要是用到imageview的这个方法:
/** * adds a transformation {@link matrix} that is applied * to the view's drawable when it is drawn. allows custom scaling, * translation, and perspective distortion. * * @param matrix the transformation parameters in matrix form */ public void setimagematrix(matrix matrix) { // collapse null and identity to just null if (matrix != null && matrix.isidentity()) { matrix = null; } // don't invalidate unless we're actually changing our matrix if (matrix == null && !mmatrix.isidentity() || matrix != null && !mmatrix.equals(matrix)) { mmatrix.set(matrix); configurebounds(); invalidate(); } }
利用的是matrix这个类(对这个类不懂的童鞋自己去查资料哈~),然后通过监听我们的ontouchevent方法获取当前手势操作,然后对matrix进行相应操作,改变图片的状态。
代码比较短,而且我每行都注释了,我就直接给代码了:
matriximageview.java:
package com.leo.gestureimageview; import android.content.context; import android.graphics.bitmap; import android.graphics.bitmapfactory; import android.graphics.matrix; import android.util.attributeset; import android.util.displaymetrics; import android.view.motionevent; import android.widget.imageview; public class matriximageview2 extends imageview { private static final int mode_none = 190; private static final int mode_drag = 468; private static final int mode_zoom = 685; //当前mode private int mode; //手指按下时候的坐标 private float startx, starty; //两个手指中间点的位置 private float midx, midy; //当前imageview的matirx对象,以前imageview的matrix对象 private matrix currmatrix, savedmatrix; //之前图片的旋转角度 private float prerotate; //之间两个手指之间的距离 private float prespacing; public matriximageview2(context context, attributeset attrs) { super(context, attrs); initview(); } private void initview() { //初始化模式为初始状态 mode = mode_none; currmatrix = new matrix(); savedmatrix = new matrix(); displaymetrics dm = getresources().getdisplaymetrics(); //给imageview设置一张图片(此处为了测试直接在imageview里面设置了一张测试图片) bitmap bitmap = bitmapfactory.decoderesource(getresources(), r.mipmap.test); bitmap = bitmap.createscaledbitmap(bitmap, dm.widthpixels, dm.heightpixels, true); setimagebitmap(bitmap); } @override public boolean ontouchevent(motionevent event) { //多点触碰如果需要监听action_pointer_down等操作的时候,必须用event.getaction() & motionevent.action_mask //而不是直接的event.getaction(); switch (event.getaction() & motionevent.action_mask) { //当一个手指按下的时候 case motionevent.action_down: //保存当前imageview的matrix对象 savedmatrix.set(currmatrix); //记录手指开始的坐标 startx = event.getx(); starty = event.gety(); //此时的状态为拖拽状态 mode = mode_drag; break; //当两个手指按下的时候(我们先不考虑很多个的情况哈,能力有限~!) case motionevent.action_pointer_down: //计算两个手指之间的距离并保存起来 prespacing = calspacing(event); //如果两个手指之间的距离大于我们指定的一个值后(改变状态为缩放) if (prespacing > 10f) { savedmatrix.set(currmatrix); mode = mode_zoom; //记录下缩放的中间坐标值 midx = (event.getx(0) + event.getx(1)) / 2; midy = (event.gety(0) + event.gety(1)) / 2; } //根据两个手指的位置计算出当前角度并保存 prerotate = calrotate(event); break; //当手指移动的时候 case motionevent.action_move: //如果之前给的状态为拖拽状态的时候 if (mode == mode_drag) { //首先把之前的matrix的状态赋给当前的matrix对象 currmatrix.set(savedmatrix); //算出手指移动的距离 float dx = event.getx() - startx; float dy = event.gety() - starty; //把手指移动的距离设置给matrix对象 currmatrix.posttranslate(dx, dy); //当状态为放大状态的时候,并且有两个手指按下的时候 } else if (mode == mode_zoom && event.getpointercount() == 2) { //首先把之前的matrix的状态赋给当前的matrix对象 currmatrix.set(savedmatrix); //计算出此时两个手指之间的距离 float spacing = calspacing(event); //如果此时两手指之间的距离大于我们给定的值 if (spacing > 10f) { //此时两手指距离/第二个手指刚按下时两手指的距离 float scale = spacing / prespacing; //把算出的缩放值给当前matrix对象,(缩放中心点为之前算出的mid) currmatrix.postscale(scale, scale, midx, midy); } //根据两手指位置算出此时的旋转角度 float rotate = calrotate(event); if (rotate != prerotate) { //算出此时需要旋转的角度 rotate = rotate - prerotate; //开始旋转图片 currmatrix.postrotate(rotate, getmeasuredwidth() / 2, getmeasuredheight() / 2); } } break; } //最后记得把当前的matrix对象给imageview setimagematrix(currmatrix); return true; } /** * 根据两手指的位置算出角度 * 勾股定理 tan0=x(两手指横坐标距离)/y(两手指纵坐标距离); * @param event * @return */ private float calrotate(motionevent event) { double x = event.getx(0) - event.getx(1); double y = event.gety(0) - event.gety(1); double radius = math.atan2(y, x); return (float) math.todegrees(radius); } /** * 两个点距离公式为d*d=(x1-x0)的平方+(y1-y0)的平方 * @param event * @return */ private float calspacing(motionevent event) { float x = event.getx(0) - event.getx(1); float y = event.gety(0) - event.gety(1); return (float) math.sqrt(math.pow(x, 2) + math.pow(y, 2)); } }
然后添加我们的布局文件:
<com.leo.gestureimageview.matriximageview android:layout_width="match_parent" android:layout_height="match_parent" android:scaletype="matrix" android:src="@mipmap/test" />
最后运行代码:
好了,虽说我们是实现了我们的手势imageview的基本功能,但是如果要处理那种多点(>两个手指)触碰,还有一些复杂的操作的时候,我们的ontouchevent里面写的代码可能就不止这么一点了(还是有点复杂的,考虑的因素太多),但如果可以把某个事件的处理单独拿出去分成很多个分支的话,还会这么复杂么?? 如果说我们的代码可以像下面这样的话,你是不是觉得很爽呢?
package com.leo.gestureimageview; import android.content.context; import android.graphics.bitmap; import android.graphics.bitmapfactory; import android.graphics.matrix; import android.graphics.pointf; import android.util.attributeset; import android.util.displaymetrics; import android.view.motionevent; import android.view.scalegesturedetector; import android.widget.imageview; import com.leo.gestureimageview.gesturedetectors.movegesturedetector; import com.leo.gestureimageview.gesturedetectors.rotategesturedetector; public class matriximageview2 extends imageview { private matrix mmatrix = new matrix(); private float mscalefactor =1f; private float mrotationdegrees = 0.f; private float mfocusx = 0.f; private float mfocusy = 0.f; private scalegesturedetector mscaledetector; private rotategesturedetector mrotatedetector; private movegesturedetector mmovedetector; public matriximageview2(context context, attributeset attrs) { super(context, attrs); initview(); } private void initview() { //初始化模式为初始状态 displaymetrics dm = getresources().getdisplaymetrics(); //给imageview设置一张图片(此处为了测试直接在imageview里面设置了一张测试图片) bitmap bitmap = bitmapfactory.decoderesource(getresources(), r.mipmap.test); bitmap = bitmap.createscaledbitmap(bitmap, dm.widthpixels, dm.heightpixels, true); setimagebitmap(bitmap); mscaledetector = new scalegesturedetector(getcontext(), new scalelistener()); mrotatedetector = new rotategesturedetector(getcontext(), new rotatelistener()); mmovedetector = new movegesturedetector(getcontext(), new movelistener()); mfocusx = dm.widthpixels/2f; mfocusy = dm.heightpixels/2f; } @override public boolean ontouchevent(motionevent event) { mscaledetector.ontouchevent(event); mrotatedetector.ontouchevent(event); mmovedetector.ontouchevent(event); return true; } private class scalelistener extends scalegesturedetector.simpleonscalegesturelistener { @override public boolean onscale(scalegesturedetector detector) { mscalefactor *= detector.getscalefactor(); // scale change since previous event // don't let the object get too small or too large. mscalefactor = math.max(0.1f, math.min(mscalefactor, 10.0f)); changematrix(); return true; } } private class rotatelistener extends rotategesturedetector.simpleonrotategesturelistener { @override public boolean onrotate(rotategesturedetector detector) { mrotationdegrees -= detector.getrotationdegreesdelta(); changematrix(); return true; } } private class movelistener extends movegesturedetector.simpleonmovegesturelistener { @override public boolean onmove(movegesturedetector detector) { pointf d = detector.getfocusdelta(); mfocusx += d.x; mfocusy += d.y; changematrix(); return true; } } private void changematrix(){ float scaledimagecenterx = (getdrawable().getintrinsicwidth()*mscalefactor)/2; float scaledimagecentery = (getdrawable().getintrinsicheight()*mscalefactor)/2; mmatrix.reset(); mmatrix.postscale(mscalefactor, mscalefactor); mmatrix.postrotate(mrotationdegrees, scaledimagecenterx, scaledimagecentery); mmatrix.posttranslate(mfocusx - scaledimagecenterx, mfocusy - scaledimagecentery); setimagematrix(mmatrix); } }
我们的imageview的ontouchevent就只剩下短短的几行代码了,然后各个detector处理完事件后,我们只需要拿到处理好的值就可以了:
@override public boolean ontouchevent(motionevent event) { //把缩放事件给mscaledetector mscaledetector.ontouchevent(event); //把旋转事件个mrotatedetector mrotatedetector.ontouchevent(event); //把移动事件给mmovedetector mmovedetector.ontouchevent(event); return true; }
是不是觉得很爽呢? 是的,我也是无意中看到了这个开源项目,先附上这个框架的github链接:
https://github.com/almeros/android-gesture-detectors
下一节我们将深入了解detector,以及系统自带的手势处理工具类gesturedetector,感兴趣的小伙伴请继续关注哦。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。