Android图片查看支持双击放大缩小、多点触摸


分类: Android 1737人阅读 评论(2) 收藏 举报

        前一段时间写的,现在分享出来供大家参考。

        研究过图库的源码,但着实太复杂,我都看不懂!也参考过网上的一些源码,但很多功能都不全,都不是我想要的! 结合网上的代码以及图库的部分源码自己写了一个类。

        未实现的功能——逐级放大,这个矩阵变换把我弄晕了,而且项目中也用不到,所以就没再研究。

        该模块主要实现了放大和原大两个级别的缩放。

 功能有:

  1. 以触摸点为中心放大(这个是网上其他的代码没有的)
  2. 边界控制(这个是网上其他的代码没有的)
  3. 双击放大或缩小(主要考虑到电阻屏)
  4. 多点触摸放大和缩小

        这个模块已经通过了测试,并且用户也使用有一段时间了,是属于比较稳定的了。

下面贴上代码及使用方法(没有写测试项目,大家见谅):

 ImageControl.cs   类似一个用户自定义的ImageView控件。用法将在下面的代码中贴出。

  1. import android.content.Context;  
  2. import android.graphics.Bitmap;  
  3. import android.graphics.Matrix;  
  4. import android.util.AttributeSet;  
  5. import android.util.FloatMath;  
  6. import android.view.MotionEvent;  
  7. import android.widget.ImageView;  
  8.   
  9. public class ImageControl extends ImageView {  
  10.     public ImageControl(Context context) {  
  11.         super(context);  
  12.         // TODO Auto-generated constructor stub  
  13.     }  
  14.   
  15.     public ImageControl(Context context, AttributeSet attrs) {  
  16.         super(context, attrs);  
  17.         // TODO Auto-generated constructor stub  
  18.     }  
  19.   
  20.     public ImageControl(Context context, AttributeSet attrs, int defStyle) {  
  21.         super(context, attrs, defStyle);  
  22.         // TODO Auto-generated constructor stub  
  23.     }  
  24.   
  25.     // ImageView img;  
  26.     Matrix imgMatrix = null// 定义图片的变换矩阵  
  27.   
  28.     static final int DOUBLE_CLICK_TIME_SPACE = 300// 双击时间间隔  
  29.     static final int DOUBLE_POINT_DISTANCE = 10// 两点放大两点间最小间距  
  30.     static final int NONE = 0;  
  31.     static final int DRAG = 1// 拖动操作  
  32.     static final int ZOOM = 2// 放大缩小操作  
  33.     private int mode = NONE; // 当前模式  
  34.   
  35.     float bigScale = 3f; // 默认放大倍数  
  36.     Boolean isBig = false// 是否是放大状态  
  37.     long lastClickTime = 0// 单击时间  
  38.     float startDistance; // 多点触摸两点距离  
  39.     float endDistance; // 多点触摸两点距离  
  40.   
  41.     float topHeight; // 状态栏高度和标题栏高度  
  42.     Bitmap primaryBitmap = null;  
  43.   
  44.     float contentW; // 屏幕内容区宽度  
  45.     float contentH; // 屏幕内容区高度  
  46.   
  47.     float primaryW; // 原图宽度  
  48.     float primaryH; // 原图高度  
  49.   
  50.     float scale; // 适合屏幕缩放倍数  
  51.     Boolean isMoveX = true// 是否允许在X轴拖动  
  52.     Boolean isMoveY = true// 是否允许在Y轴拖动  
  53.     float startX;  
  54.     float startY;  
  55.     float endX;  
  56.     float endY;  
  57.     float subX;  
  58.     float subY;  
  59.     float limitX1;  
  60.     float limitX2;  
  61.     float limitY1;  
  62.     float limitY2;  
  63.     ICustomMethod mCustomMethod = null;  
  64.   
  65.     /** 
  66.      * 初始化图片 
  67.      *  
  68.      * @param bitmap 
  69.      *            要显示的图片 
  70.      * @param contentW 
  71.      *            内容区域宽度 
  72.      * @param contentH 
  73.      *            内容区域高度 
  74.      * @param topHeight 
  75.      *            状态栏高度和标题栏高度之和 
  76.      */  
  77.     public void imageInit(Bitmap bitmap, int contentW, int contentH,  
  78.             int topHeight, ICustomMethod iCustomMethod) {  
  79.         this.primaryBitmap = bitmap;  
  80.         this.contentW = contentW;  
  81.         this.contentH = contentH;  
  82.         this.topHeight = topHeight;  
  83.         mCustomMethod = iCustomMethod;  
  84.         primaryW = primaryBitmap.getWidth();  
  85.         primaryH = primaryBitmap.getHeight();  
  86.         float scaleX = (float) contentW / primaryW;  
  87.         float scaleY = (float) contentH / primaryH;  
  88.         scale = scaleX < scaleY ? scaleX : scaleY;  
  89.         if (scale < 1 && 1 / scale < bigScale) {  
  90.             bigScale = (float) (1 / scale + 0.5);  
  91.         }  
  92.   
  93.         imgMatrix = new Matrix();  
  94.         subX = (contentW - primaryW * scale) / 2;  
  95.         subY = (contentH - primaryH * scale) / 2;  
  96.         this.setImageBitmap(primaryBitmap);  
  97.         this.setScaleType(ScaleType.MATRIX);  
  98.         imgMatrix.postScale(scale, scale);  
  99.         imgMatrix.postTranslate(subX, subY);  
  100.         this.setImageMatrix(imgMatrix);  
  101.     }  
  102.   
  103.     /** 
  104.      * 按下操作 
  105.      *  
  106.      * @param event 
  107.      */  
  108.     public void mouseDown(MotionEvent event) {  
  109.         mode = NONE;  
  110.         startX = event.getRawX();  
  111.         startY = event.getRawY();  
  112.         if (event.getPointerCount() == 1) {  
  113.             // 如果两次点击时间间隔小于一定值,则默认为双击事件  
  114.             if (event.getEventTime() - lastClickTime < DOUBLE_CLICK_TIME_SPACE) {  
  115.                 changeSize(startX, startY);  
  116.             } else if (isBig) {  
  117.                 mode = DRAG;  
  118.             }  
  119.         }  
  120.   
  121.         lastClickTime = event.getEventTime();  
  122.     }  
  123.   
  124.     /** 
  125.      * 非第一个点按下操作 
  126.      *  
  127.      * @param event 
  128.      */  
  129.     public void mousePointDown(MotionEvent event) {  
  130.         startDistance = getDistance(event);  
  131.         if (startDistance > DOUBLE_POINT_DISTANCE) {  
  132.             mode = ZOOM;  
  133.         } else {  
  134.             mode = NONE;  
  135.         }  
  136.     }  
  137.   
  138.     /** 
  139.      * 移动操作 
  140.      *  
  141.      * @param event 
  142.      */  
  143.     public void mouseMove(MotionEvent event) {  
  144.         if ((mode == DRAG) && (isMoveX || isMoveY)) {  
  145.             float[] XY = getTranslateXY(imgMatrix);  
  146.             float transX = 0;  
  147.             float transY = 0;  
  148.             if (isMoveX) {  
  149.                 endX = event.getRawX();  
  150.                 transX = endX - startX;  
  151.                 if ((XY[0] + transX) <= limitX1) {  
  152.                     transX = limitX1 - XY[0];  
  153.                 }  
  154.                 if ((XY[0] + transX) >= limitX2) {  
  155.                     transX = limitX2 - XY[0];  
  156.                 }  
  157.             }  
  158.             if (isMoveY) {  
  159.                 endY = event.getRawY();  
  160.                 transY = endY - startY;  
  161.                 if ((XY[1] + transY) <= limitY1) {  
  162.                     transY = limitY1 - XY[1];  
  163.                 }  
  164.                 if ((XY[1] + transY) >= limitY2) {  
  165.                     transY = limitY2 - XY[1];  
  166.                 }  
  167.             }  
  168.   
  169.             imgMatrix.postTranslate(transX, transY);  
  170.             startX = endX;  
  171.             startY = endY;  
  172.             this.setImageMatrix(imgMatrix);  
  173.         } else if (mode == ZOOM && event.getPointerCount() > 1) {  
  174.             endDistance = getDistance(event);  
  175.             float dif = endDistance - startDistance;  
  176.             if (Math.abs(endDistance - startDistance) > DOUBLE_POINT_DISTANCE) {  
  177.                 if (isBig) {  
  178.                     if (dif < 0) {  
  179.                         changeSize(00);  
  180.                         mode = NONE;  
  181.                     }  
  182.                 } else if (dif > 0) {  
  183.                     float x = event.getX(0) / 2 + event.getX(1) / 2;  
  184.                     float y = event.getY(0) / 2 + event.getY(1) / 2;  
  185.                     changeSize(x, y);  
  186.                     mode = NONE;  
  187.                 }  
  188.             }  
  189.         }  
  190.     }  
  191.   
  192.     /** 
  193.      * 鼠标抬起事件 
  194.      */  
  195.     public void mouseUp() {  
  196.         mode = NONE;  
  197.     }  
  198.   
  199.     /** 
  200.      * 图片放大缩小 
  201.      *  
  202.      * @param x 
  203.      *            点击点X坐标 
  204.      * @param y 
  205.      *            点击点Y坐标 
  206.      */  
  207.     private void changeSize(float x, float y) {  
  208.         if (isBig) {  
  209.             // 如果处于最大状态,则还原  
  210.             imgMatrix.reset();  
  211.             imgMatrix.postScale(scale, scale);  
  212.             imgMatrix.postTranslate(subX, subY);  
  213.             isBig = false;  
  214.         } else {  
  215.             imgMatrix.postScale(bigScale, bigScale); // 在原有矩阵后乘放大倍数  
  216.             float transX = -((bigScale - 1) * x);  
  217.             float transY = -((bigScale - 1) * (y - topHeight)); // (bigScale-1)(y-statusBarHeight-subY)+2*subY;  
  218.             float currentWidth = primaryW * scale * bigScale; // 放大后图片大小  
  219.             float currentHeight = primaryH * scale * bigScale;  
  220.             // 如果图片放大后超出屏幕范围处理  
  221.             if (currentHeight > contentH) {  
  222.                 limitY1 = -(currentHeight - contentH); // 平移限制  
  223.                 limitY2 = 0;  
  224.                 isMoveY = true// 允许在Y轴上拖动  
  225.                 float currentSubY = bigScale * subY; // 当前平移距离  
  226.                 // 平移后,内容区域上部有空白处理办法  
  227.                 if (-transY < currentSubY) {  
  228.                     transY = -currentSubY;  
  229.                 }  
  230.                 // 平移后,内容区域下部有空白处理办法  
  231.                 if (currentSubY + transY < limitY1) {  
  232.                     transY = -(currentHeight + currentSubY - contentH);  
  233.                 }  
  234.             } else {  
  235.                 // 如果图片放大后没有超出屏幕范围处理,则不允许拖动  
  236.                 isMoveY = false;  
  237.             }  
  238.   
  239.             if (currentWidth > contentW) {  
  240.                 limitX1 = -(currentWidth - contentW);  
  241.                 limitX2 = 0;  
  242.                 isMoveX = true;  
  243.                 float currentSubX = bigScale * subX;  
  244.                 if (-transX < currentSubX) {  
  245.                     transX = -currentSubX;  
  246.                 }  
  247.                 if (currentSubX + transX < limitX1) {  
  248.                     transX = -(currentWidth + currentSubX - contentW);  
  249.                 }  
  250.             } else {  
  251.                 isMoveX = false;  
  252.             }  
  253.   
  254.             imgMatrix.postTranslate(transX, transY);  
  255.             isBig = true;  
  256.         }  
  257.   
  258.         this.setImageMatrix(imgMatrix);  
  259.         if (mCustomMethod != null) {  
  260.             mCustomMethod.customMethod(isBig);  
  261.         }  
  262.     }  
  263.   
  264.     /** 
  265.      * 获取变换矩阵中X轴偏移量和Y轴偏移量 
  266.      *  
  267.      * @param matrix 
  268.      *            变换矩阵 
  269.      * @return 
  270.      */  
  271.     private float[] getTranslateXY(Matrix matrix) {  
  272.         float[] values = new float[9];  
  273.         matrix.getValues(values);  
  274.         float[] floats = new float[2];  
  275.         floats[0] = values[Matrix.MTRANS_X];  
  276.         floats[1] = values[Matrix.MTRANS_Y];  
  277.         return floats;  
  278.     }  
  279.   
  280.     /** 
  281.      * 获取两点间的距离 
  282.      *  
  283.      * @param event 
  284.      * @return 
  285.      */  
  286.     private float getDistance(MotionEvent event) {  
  287.         float x = event.getX(0) - event.getX(1);  
  288.         float y = event.getY(0) - event.getY(1);  
  289.         return FloatMath.sqrt(x * x + y * y);  
  290.     }  
  291.   
  292.     /** 
  293.      * @author Administrator 用户自定义方法 
  294.      */  
  295.     public interface ICustomMethod {  
  296.         public void customMethod(Boolean currentStatus);  
  297.     }  
  298. }  

 

ImageVewActivity.cs   这个用于测试的Activity

  1. import android.app.Activity;  
  2. import android.graphics.Bitmap;  
  3. import android.graphics.Rect;  
  4. import android.graphics.drawable.BitmapDrawable;  
  5. import android.os.Bundle;  
  6. import android.view.MotionEvent;  
  7. import android.view.View;  
  8. import android.widget.LinearLayout;  
  9. import android.widget.TextView;  
  10. import android.widget.Toast;  
  11. import ejiang.boiler.ImageControl.ICustomMethod;  
  12. import ejiang.boiler.R.id;  
  13.   
  14. public class ImageViewActivity extends Activity {  
  15.   
  16.     @Override  
  17.     protected void onCreate(Bundle savedInstanceState) {  
  18.         // TODO Auto-generated method stub  
  19.         super.onCreate(savedInstanceState);  
  20.         setContentView(R.layout.common_image_view);  
  21.         findView();  
  22.     }  
  23.   
  24.     public void onWindowFocusChanged(boolean hasFocus) {  
  25.         super.onWindowFocusChanged(hasFocus);  
  26.         init();  
  27.     }  
  28.   
  29.     ImageControl imgControl;  
  30.     LinearLayout llTitle;  
  31.     TextView tvTitle;  
  32.   
  33.     private void findView() {  
  34.         imgControl = (ImageControl) findViewById(id.common_imageview_imageControl1);  
  35.         llTitle = (LinearLayout) findViewById(id.common_imageview_llTitle);  
  36.         tvTitle = (TextView) findViewById(id.common_imageview_title);  
  37.     }  
  38.   
  39.     private void init() {  
  40.         tvTitle.setText("图片测试");  
  41.         // 这里可以为imgcontrol的图片路径动态赋值  
  42.         // ............  
  43.           
  44.         Bitmap bmp;  
  45.         if (imgControl.getDrawingCache() != null) {  
  46.             bmp = Bitmap.createBitmap(imgControl.getDrawingCache());  
  47.         } else {  
  48.             bmp = ((BitmapDrawable) imgControl.getDrawable()).getBitmap();  
  49.         }  
  50.         Rect frame = new Rect();  
  51.         getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);  
  52.         int statusBarHeight = frame.top;  
  53.         int screenW = this.getWindowManager().getDefaultDisplay().getWidth();  
  54.         int screenH = this.getWindowManager().getDefaultDisplay().getHeight()  
  55.                 - statusBarHeight;  
  56.         if (bmp != null) {  
  57.             imgControl.imageInit(bmp, screenW, screenH, statusBarHeight,  
  58.                     new ICustomMethod() {  
  59.                         
  60.                         @Override  
  61.                         public void customMethod(Boolean currentStatus) {  
  62.                             // 当图片处于放大或缩小状态时,控制标题是否显示  
  63.                             if (currentStatus) {  
  64.                                 llTitle.setVisibility(View.GONE);  
  65.                             } else {  
  66.                                 llTitle.setVisibility(View.VISIBLE);  
  67.                             }  
  68.                         }  
  69.                     });  
  70.         }  
  71.         else  
  72.         {  
  73.             Toast.makeText(ImageViewActivity.this"图片加载失败,请稍候再试!", Toast.LENGTH_SHORT)  
  74.                     .show();  
  75.         }  
  76.   
  77.     }  
  78.   
  79.     @Override  
  80.     public boolean onTouchEvent(MotionEvent event) {  
  81.         switch (event.getAction() & MotionEvent.ACTION_MASK) {  
  82.         case MotionEvent.ACTION_DOWN:  
  83.             imgControl.mouseDown(event);              
  84.             break;  
  85.   
  86.         /** 
  87.          * 非第一个点按下 
  88.          */  
  89.         case MotionEvent.ACTION_POINTER_DOWN:  
  90.           
  91.                 imgControl.mousePointDown(event);  
  92.           
  93.             break;  
  94.         case MotionEvent.ACTION_MOVE:  
  95.                 imgControl.mouseMove(event);  
  96.               
  97.             break;  
  98.   
  99.         case MotionEvent.ACTION_UP:  
  100.             imgControl.mouseUp();  
  101.             break;  
  102.   
  103.         }  
  104.   
  105.         return super.onTouchEvent(event);  
  106.     }  
  107. }  

        在上面的代码中,需要注意两点。一Activity中要重写onTouchEvent方法,将触摸事件传递到ImageControl,这点类似于WPF中的路由事件机制。二初始化imgControl即imgControl.imageInit,注意其中的参数。最后一个参数类似于C#中的委托,我这里使用接口来实现,在放大缩小的切换时要执行的操作都卸载这个方法中。


common_image_view.xml  布局文件

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:id="@+id/rl"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent" >  
  6.   
  7.     <ejiang.boiler.ImageControl  
  8.         android:id="@+id/common_imageview_imageControl1"  
  9.         android:layout_width="fill_parent"  
  10.         android:layout_height="fill_parent"  
  11.         android:src="@drawable/ic_launcher" />  
  12.   
  13.     <LinearLayout  
  14.         android:id="@+id/common_imageview_llTitle"  
  15.         style="@style/reportTitle1"  
  16.         android:layout_alignParentLeft="true"  
  17.         android:layout_alignParentTop="true" >  
  18.   
  19.         <TextView  
  20.             android:id="@+id/common_imageview_title"  
  21.             style="@style/title2"  
  22.             android:layout_width="fill_parent"  
  23.             android:layout_height="wrap_content"  
  24.             android:layout_weight="1"  
  25.             android:text="报告" />  
  26.     </LinearLayout>  
  27.   
  28. </RelativeLayout>  


        我学习android的时间也不长,因此有什么纰漏或错误,欢迎大家指出!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值