可缩放性ImageView(可以放大缩小)

由于项目需求的原因,最近一直在研究可缩放性ImageView,用本文来记录一下最近所学:

该ImageView的实现功能有:

1)初步打开时,图片按比例满屏(填充ImageView)显示。

2)在放大缩小过程中,可以控制最大放大比例和最小缩小比例。

3)在缩放过程中,若图片的宽或高小于ImageView,则在图片在宽或高居中显示。

4)在放大后,可以移动图片,并且限制好移动的边界,不会超出图片。

5)实现双击放大或缩小的功能。(若当前图片显示为最大的比例则缩小为最小比例,若不是最小比例则放大了最大比例)

在讲代码之前,首先应该说说一个类,Matrix。因为我们在处理图片的过程中,需要图片的位移,缩放等等,而Matrix刚好就是帮我们封装好了这些数据,具体的,大家可以看看这篇文章:android 从matrix获取处理过的图片的实际宽度重点是了解Matrix里面数组的含义。

上代码,具体的说明代码都有一一介绍:

MyZoomImageView.java文件:

[java]  view plain  copy
  1. package com.xiaoyan.doubletouch;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Matrix;  
  5. import android.graphics.PointF;  
  6. import android.graphics.drawable.Drawable;  
  7. import android.util.AttributeSet;  
  8. import android.util.FloatMath;  
  9. import android.view.MotionEvent;  
  10. import android.view.View;  
  11. import android.view.ViewTreeObserver;  
  12. import android.view.ViewTreeObserver.OnGlobalLayoutListener;  
  13. import android.widget.ImageView;  
  14.   
  15. /** 
  16.  * 缩放ImageView 
  17.  *  
  18.  * @author xiejinxiong 
  19.  *  
  20.  */  
  21. public class MyZoomImageView extends ImageView {  
  22.   
  23.     /** ImageView高度 */  
  24.     private int imgHeight;  
  25.     /** ImageView宽度 */  
  26.     private int imgWidth;  
  27.     /** 图片高度 */  
  28.     private int intrinsicHeight;  
  29.     /** 图片宽度 */  
  30.     private int intrinsicWidth;  
  31.     /** 最大缩放级别 */  
  32.     private float mMaxScale = 2.0f;  
  33.     /** 最小缩放级别 */  
  34.     private float mMinScale = 0.2f;  
  35.     /** 用于记录拖拉图片移动的坐标位置 */  
  36.     private Matrix matrix = new Matrix();  
  37.     /** 用于记录图片要进行拖拉时候的坐标位置 */  
  38.     private Matrix currentMatrix = new Matrix();  
  39.     /** 记录第一次点击的时间 */  
  40.     private long firstTouchTime = 0;  
  41.     /** 时间点击的间隔 */  
  42.     private int intervalTime = 250;  
  43.     /** 第一次点完坐标 */  
  44.     private PointF firstPointF;  
  45.   
  46.     public MyZoomImageView(Context context) {  
  47.         super(context);  
  48.         initUI();  
  49.     }  
  50.   
  51.     public MyZoomImageView(Context context, AttributeSet attrs) {  
  52.         super(context, attrs);  
  53.         initUI();  
  54.     }  
  55.   
  56.     public MyZoomImageView(Context context, AttributeSet attrs, int defStyle) {  
  57.         super(context, attrs, defStyle);  
  58.         // TODO Auto-generated constructor stub  
  59.         initUI();  
  60.     }  
  61.   
  62.     /** 
  63.      * 初始化UI 
  64.      */  
  65.     private void initUI() {  
  66.   
  67.         this.setScaleType(ScaleType.FIT_CENTER);  
  68.         this.setOnTouchListener(new TouchListener());  
  69.   
  70.         getImageViewWidthHeight();  
  71.         getIntrinsicWidthHeight();  
  72.     }  
  73.   
  74.     /** 
  75.      * 获得图片内在宽高 
  76.      */  
  77.     private void getIntrinsicWidthHeight() {  
  78.         Drawable drawable = this.getDrawable();  
  79.   
  80.         // 初始化bitmap的宽高  
  81.         intrinsicHeight = drawable.getIntrinsicHeight();  
  82.         intrinsicWidth = drawable.getIntrinsicWidth();  
  83.     }  
  84.   
  85.     private final class TouchListener implements OnTouchListener {  
  86.   
  87.         /** 记录是拖拉照片模式还是放大缩小照片模式 */  
  88.         private int mode = 0;// 初始状态  
  89.         /** 拖拉照片模式 */  
  90.         private static final int MODE_DRAG = 1;  
  91.         /** 放大缩小照片模式 */  
  92.         private static final int MODE_ZOOM = 2;  
  93.         /** 用于记录开始时候的坐标位置 */  
  94.         private PointF startPoint = new PointF();  
  95.         /** 两个手指的开始距离 */  
  96.         private float startDis;  
  97.         /** 两个手指的中间点 */  
  98.         private PointF midPoint;  
  99.   
  100.         public boolean onTouch(View v, MotionEvent event) {  
  101.   
  102.             /** 通过与运算保留最后八位 MotionEvent.ACTION_MASK = 255 */  
  103.             switch (event.getAction() & MotionEvent.ACTION_MASK) {// 单点监听和多点触碰监听  
  104.             // 手指压下屏幕  
  105.             case MotionEvent.ACTION_DOWN:  
  106.                 mode = MODE_DRAG;  
  107.                 // 记录ImageView当前的移动位置  
  108.                 currentMatrix.set(getImageMatrix());  
  109.                 startPoint.set(event.getX(), event.getY());  
  110.                 matrix.set(currentMatrix);  
  111.                 makeImageViewFit();  
  112.                 break;  
  113.             // 手指在屏幕上移动,改事件会被不断触发  
  114.             case MotionEvent.ACTION_MOVE:  
  115.                 // 拖拉图片  
  116.                 if (mode == MODE_DRAG) {  
  117.                     // System.out.println("ACTION_MOVE_____MODE_DRAG");  
  118.                     float dx = event.getX() - startPoint.x; // 得到x轴的移动距离  
  119.                     float dy = event.getY() - startPoint.y; // 得到x轴的移动距离  
  120.                     // 在没有移动之前的位置上进行移动z  
  121.                     matrix.set(currentMatrix);  
  122.                     float[] values = new float[9];  
  123.                     matrix.getValues(values);  
  124.                     dx = checkDxBound(values, dx);  
  125.                     dy = checkDyBound(values, dy);  
  126.                     matrix.postTranslate(dx, dy);  
  127.   
  128.                 }  
  129.                 // 放大缩小图片  
  130.                 else if (mode == MODE_ZOOM) {  
  131.                     float endDis = distance(event);// 结束距离  
  132.                     if (endDis > 10f) { // 两个手指并拢在一起的时候像素大于10  
  133.                         float scale = endDis / startDis;// 得到缩放倍数  
  134.                         matrix.set(currentMatrix);  
  135.   
  136.                         float[] values = new float[9];  
  137.                         matrix.getValues(values);  
  138.   
  139.                         scale = checkFitScale(scale, values);  
  140.   
  141.                         matrix.postScale(scale, scale, midPoint.x, midPoint.y);  
  142.   
  143.                     }  
  144.                 }  
  145.                 break;  
  146.             // 手指离开屏幕  
  147.             case MotionEvent.ACTION_UP:  
  148.                 setDoubleTouchEvent(event);  
  149.   
  150.             case MotionEvent.ACTION_POINTER_UP:  
  151.                 // System.out.println("ACTION_POINTER_UP");  
  152.                 mode = 0;  
  153.                 // matrix.set(currentMatrix);  
  154.                 float[] values = new float[9];  
  155.                 matrix.getValues(values);  
  156.                 makeImgCenter(values);  
  157.                 break;  
  158.             // 当屏幕上已经有触点(手指),再有一个触点压下屏幕  
  159.             case MotionEvent.ACTION_POINTER_DOWN:  
  160.                 // System.out.println("ACTION_POINTER_DOWN");  
  161.                 mode = MODE_ZOOM;  
  162.                 /** 计算两个手指间的距离 */  
  163.                 startDis = distance(event);  
  164.                 /** 计算两个手指间的中间点 */  
  165.                 if (startDis > 10f) { // 两个手指并拢在一起的时候像素大于10  
  166.                     midPoint = mid(event);  
  167.                     // 记录当前ImageView的缩放倍数  
  168.                     currentMatrix.set(getImageMatrix());  
  169.                 }  
  170.                 break;  
  171.             }  
  172.             setImageMatrix(matrix);  
  173.             return true;  
  174.         }  
  175.   
  176.         /** 计算两个手指间的距离 */  
  177.         private float distance(MotionEvent event) {  
  178.             float dx = event.getX(1) - event.getX(0);  
  179.             float dy = event.getY(1) - event.getY(0);  
  180.             /** 使用勾股定理返回两点之间的距离 */  
  181.             return FloatMath.sqrt(dx * dx + dy * dy);  
  182.         }  
  183.   
  184.         /** 计算两个手指间的中间点 */  
  185.         private PointF mid(MotionEvent event) {  
  186.             float midX = (event.getX(1) + event.getX(0)) / 2;  
  187.             float midY = (event.getY(1) + event.getY(0)) / 2;  
  188.             return new PointF(midX, midY);  
  189.         }  
  190.   
  191.         /** 
  192.          * 和当前矩阵对比,检验dy,使图像移动后不会超出ImageView边界 
  193.          *  
  194.          * @param values 
  195.          * @param dy 
  196.          * @return 
  197.          */  
  198.         private float checkDyBound(float[] values, float dy) {  
  199.   
  200.             float height = imgHeight;  
  201.             if (intrinsicHeight * values[Matrix.MSCALE_Y] < height)  
  202.                 return 0;  
  203.             if (values[Matrix.MTRANS_Y] + dy > 0)  
  204.                 dy = -values[Matrix.MTRANS_Y];  
  205.             else if (values[Matrix.MTRANS_Y] + dy < -(intrinsicHeight  
  206.                     * values[Matrix.MSCALE_Y] - height))  
  207.                 dy = -(intrinsicHeight * values[Matrix.MSCALE_Y] - height)  
  208.                         - values[Matrix.MTRANS_Y];  
  209.             return dy;  
  210.         }  
  211.   
  212.         /** 
  213.          * 和当前矩阵对比,检验dx,使图像移动后不会超出ImageView边界 
  214.          *  
  215.          * @param values 
  216.          * @param dx 
  217.          * @return 
  218.          */  
  219.         private float checkDxBound(float[] values, float dx) {  
  220.   
  221.             float width = imgWidth;  
  222.             if (intrinsicWidth * values[Matrix.MSCALE_X] < width)  
  223.                 return 0;  
  224.             if (values[Matrix.MTRANS_X] + dx > 0)  
  225.                 dx = -values[Matrix.MTRANS_X];  
  226.             else if (values[Matrix.MTRANS_X] + dx < -(intrinsicWidth  
  227.                     * values[Matrix.MSCALE_X] - width))  
  228.                 dx = -(intrinsicWidth * values[Matrix.MSCALE_X] - width)  
  229.                         - values[Matrix.MTRANS_X];  
  230.             return dx;  
  231.         }  
  232.   
  233.         /** 
  234.          * MSCALE用于处理缩放变换 
  235.          *  
  236.          *  
  237.          * MSKEW用于处理错切变换 
  238.          *  
  239.          *  
  240.          * MTRANS用于处理平移变换 
  241.          */  
  242.   
  243.         /** 
  244.          * 检验scale,使图像缩放后不会超出最大倍数 
  245.          *  
  246.          * @param scale 
  247.          * @param values 
  248.          * @return 
  249.          */  
  250.         private float checkFitScale(float scale, float[] values) {  
  251.             if (scale * values[Matrix.MSCALE_X] > mMaxScale)  
  252.                 scale = mMaxScale / values[Matrix.MSCALE_X];  
  253.             if (scale * values[Matrix.MSCALE_X] < mMinScale)  
  254.                 scale = mMinScale / values[Matrix.MSCALE_X];  
  255.             return scale;  
  256.         }  
  257.   
  258.         /** 
  259.          * 促使图片居中 
  260.          *  
  261.          * @param values 
  262.          *            (包含着图片变化信息) 
  263.          */  
  264.         private void makeImgCenter(float[] values) {  
  265.   
  266.             // 缩放后图片的宽高  
  267.             float zoomY = intrinsicHeight * values[Matrix.MSCALE_Y];  
  268.             float zoomX = intrinsicWidth * values[Matrix.MSCALE_X];  
  269.             // 图片左上角Y坐标  
  270.             float leftY = values[Matrix.MTRANS_Y];  
  271.             // 图片左上角X坐标  
  272.             float leftX = values[Matrix.MTRANS_X];  
  273.             // 图片右下角Y坐标  
  274.             float rightY = leftY + zoomY;  
  275.             // 图片右下角X坐标  
  276.             float rightX = leftX + zoomX;  
  277.   
  278.             // 使图片垂直居中  
  279.             if (zoomY < imgHeight) {  
  280.                 float marY = (imgHeight - zoomY) / 2.0f;  
  281.                 matrix.postTranslate(0, marY - leftY);  
  282.             }  
  283.   
  284.             // 使图片水平居中  
  285.             if (zoomX < imgWidth) {  
  286.   
  287.                 float marX = (imgWidth - zoomX) / 2.0f;  
  288.                 matrix.postTranslate(marX - leftX, 0);  
  289.   
  290.             }  
  291.   
  292.             // 使图片缩放后上下不留白(即当缩放后图片的大小大于imageView的大小,但是上面或下面留出一点空白的话,将图片移动占满空白处)  
  293.             if (zoomY >= imgHeight) {  
  294.                 if (leftY > 0) {// 判断图片上面留白  
  295.                     matrix.postTranslate(0, -leftY);  
  296.                 }  
  297.                 if (rightY < imgHeight) {// 判断图片下面留白  
  298.                     matrix.postTranslate(0, imgHeight - rightY);  
  299.                 }  
  300.             }  
  301.   
  302.             // 使图片缩放后左右不留白  
  303.             if (zoomX >= imgWidth) {  
  304.                 if (leftX > 0) {// 判断图片左边留白  
  305.                     matrix.postTranslate(-leftX, 0);  
  306.                 }  
  307.                 if (rightX < imgWidth) {// 判断图片右边不留白  
  308.                     matrix.postTranslate(imgWidth - rightX, 0);  
  309.                 }  
  310.             }  
  311.         }  
  312.   
  313.     }  
  314.   
  315.     /** 
  316.      * 获取ImageView的宽高 
  317.      */  
  318.     private void getImageViewWidthHeight() {  
  319.         ViewTreeObserver vto2 = getViewTreeObserver();  
  320.         vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {  
  321.             @SuppressWarnings("deprecation")  
  322.             public void onGlobalLayout() {  
  323.                 getViewTreeObserver().removeGlobalOnLayoutListener(this);  
  324.                 imgWidth = getWidth();  
  325.                 imgHeight = getHeight();  
  326.   
  327.             }  
  328.         });  
  329.     }  
  330.   
  331.     /** 
  332.      * 使得ImageView一开始便显示最适合的宽高比例,便是刚好容下的样子 
  333.      */  
  334.     private void makeImageViewFit() {  
  335.         if (getScaleType() != ScaleType.MATRIX) {  
  336.             setScaleType(ScaleType.MATRIX);  
  337.   
  338.             matrix.postScale(1.0f, 1.0f, imgWidth / 2, imgHeight / 2);  
  339.         }  
  340.     }  
  341.   
  342.     /** 
  343.      * 双击事件触发 
  344.      *  
  345.      * @param values 
  346.      */  
  347.     private void setDoubleTouchEvent(MotionEvent event) {  
  348.   
  349.         float values[] = new float[9];  
  350.         matrix.getValues(values);  
  351.         // 存储当前时间  
  352.         long currentTime = System.currentTimeMillis();  
  353.         // 判断两次点击间距时间是否符合  
  354.         if (currentTime - firstTouchTime >= intervalTime) {  
  355.             firstTouchTime = currentTime;  
  356.             firstPointF = new PointF(event.getX(), event.getY());  
  357.         } else {  
  358.             // 判断两次点击之间的距离是否小于30f  
  359.             if (Math.abs(event.getX() - firstPointF.x) < 30f  
  360.                     && Math.abs(event.getY() - firstPointF.y) < 30f) {  
  361.                 // 判断当前缩放比例与最大最小的比例  
  362.                 if (values[Matrix.MSCALE_X] < mMaxScale) {  
  363.                     matrix.postScale(mMaxScale / values[Matrix.MSCALE_X],  
  364.                             mMaxScale / values[Matrix.MSCALE_X], event.getX(),  
  365.                             event.getY());  
  366.                 } else {  
  367.                     matrix.postScale(mMinScale / values[Matrix.MSCALE_X],  
  368.                             mMinScale / values[Matrix.MSCALE_X], event.getX(),  
  369.                             event.getY());  
  370.                 }  
  371.             }  
  372.   
  373.         }  
  374.     }  
  375.   
  376.     /** 
  377.      * 设置图片的最大和最小的缩放比例 
  378.      *  
  379.      * @param mMaxScale 
  380.      * @param mMinScale 
  381.      */  
  382.     public void setPicZoomHeightWidth(float mMaxScale, float mMinScale) {  
  383.         this.mMaxScale = mMaxScale;  
  384.         this.mMinScale = mMinScale;  
  385.     }  
  386.   
  387. }  
xml文件:

[html]  view plain  copy
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.      xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     tools:context="com.xiaoyan.doubletouch.MainActivity" >  
  6.   
  7.     <com.xiaoyan.doubletouch.MyZoomImageView  
  8.         android:id="@+id/imageView"  
  9.         android:layout_width="fill_parent"  
  10.         android:layout_height="fill_parent"  
  11.         android:src="@drawable/a" />  
  12.   
  13. </RelativeLayout>  

具体实现效果:

最初显示:


放大显示:

猜你喜欢

转载自blog.csdn.net/Lin_XiJun/article/details/79278787
今日推荐