首页 > 系统 > Android > 正文

Android App中实现可以双击放大和缩小图片功能的实例

2020-01-02 06:59:47
字体:
来源:转载
供稿:网友

先来看一个很简单的核心图片缩放方法:

public static Bitmap scale(Bitmap bitmap, float scaleWidth, float scaleHeight) {   int width = bitmap.getWidth();   int height = bitmap.getHeight();   Matrix matrix = new Matrix();   matrix.postScale(scaleWidth, scaleHeight);   Log.i(TAG, "scaleWidth:"+ scaleWidth +", scaleHeight:"+ scaleHeight);   return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true); } 

注意要比例设置正确否则可能会内存溢出,比如曾经使用图片缩放时遇到这么个问题:

java.lang.IllegalArgumentException: bitmap size exceeds 32bits

后来一行行查代码,发现原来是 scale 的比例计算错误,将原图给放大了 20 多倍,导致内存溢出所致,重新修改比例值后就正常了。

好了,下面真正来看一下这个实现了放大和原大两个级别的缩放的模块。
功能有:

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

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

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

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

import android.content.Context; import android.graphics.Bitmap; import android.graphics.Matrix; import android.util.AttributeSet; import android.util.FloatMath; import android.view.MotionEvent; import android.widget.ImageView;  public class ImageControl extends ImageView {   public ImageControl(Context context) {     super(context);     // TODO Auto-generated constructor stub   }    public ImageControl(Context context, AttributeSet attrs) {     super(context, attrs);     // TODO Auto-generated constructor stub   }    public ImageControl(Context context, AttributeSet attrs, int defStyle) {     super(context, attrs, defStyle);     // TODO Auto-generated constructor stub   }    // ImageView img;   Matrix imgMatrix = null; // 定义图片的变换矩阵    static final int DOUBLE_CLICK_TIME_SPACE = 300; // 双击时间间隔   static final int DOUBLE_POINT_DISTANCE = 10; // 两点放大两点间最小间距   static final int NONE = 0;   static final int DRAG = 1; // 拖动操作   static final int ZOOM = 2; // 放大缩小操作   private int mode = NONE; // 当前模式    float bigScale = 3f; // 默认放大倍数   Boolean isBig = false; // 是否是放大状态   long lastClickTime = 0; // 单击时间   float startDistance; // 多点触摸两点距离   float endDistance; // 多点触摸两点距离    float topHeight; // 状态栏高度和标题栏高度   Bitmap primaryBitmap = null;    float contentW; // 屏幕内容区宽度   float contentH; // 屏幕内容区高度    float primaryW; // 原图宽度   float primaryH; // 原图高度    float scale; // 适合屏幕缩放倍数   Boolean isMoveX = true; // 是否允许在X轴拖动   Boolean isMoveY = true; // 是否允许在Y轴拖动   float startX;   float startY;   float endX;   float endY;   float subX;   float subY;   float limitX1;   float limitX2;   float limitY1;   float limitY2;   ICustomMethod mCustomMethod = null;    /**    * 初始化图片    *    * @param bitmap    *      要显示的图片    * @param contentW    *      内容区域宽度    * @param contentH    *      内容区域高度    * @param topHeight    *      状态栏高度和标题栏高度之和    */   public void imageInit(Bitmap bitmap, int contentW, int contentH,       int topHeight, ICustomMethod iCustomMethod) {     this.primaryBitmap = bitmap;     this.contentW = contentW;     this.contentH = contentH;     this.topHeight = topHeight;     mCustomMethod = iCustomMethod;     primaryW = primaryBitmap.getWidth();     primaryH = primaryBitmap.getHeight();     float scaleX = (float) contentW / primaryW;     float scaleY = (float) contentH / primaryH;     scale = scaleX < scaleY ? scaleX : scaleY;     if (scale < 1 && 1 / scale < bigScale) {       bigScale = (float) (1 / scale + 0.5);     }      imgMatrix = new Matrix();     subX = (contentW - primaryW * scale) / 2;     subY = (contentH - primaryH * scale) / 2;     this.setImageBitmap(primaryBitmap);     this.setScaleType(ScaleType.MATRIX);     imgMatrix.postScale(scale, scale);     imgMatrix.postTranslate(subX, subY);     this.setImageMatrix(imgMatrix);   }    /**    * 按下操作    *    * @param event    */   public void mouseDown(MotionEvent event) {     mode = NONE;     startX = event.getRawX();     startY = event.getRawY();     if (event.getPointerCount() == 1) {       // 如果两次点击时间间隔小于一定值,则默认为双击事件       if (event.getEventTime() - lastClickTime < DOUBLE_CLICK_TIME_SPACE) {         changeSize(startX, startY);       } else if (isBig) {         mode = DRAG;       }     }      lastClickTime = event.getEventTime();   }    /**    * 非第一个点按下操作    *    * @param event    */   public void mousePointDown(MotionEvent event) {     startDistance = getDistance(event);     if (startDistance > DOUBLE_POINT_DISTANCE) {       mode = ZOOM;     } else {       mode = NONE;     }   }    /**    * 移动操作    *    * @param event    */   public void mouseMove(MotionEvent event) {     if ((mode == DRAG) && (isMoveX || isMoveY)) {       float[] XY = getTranslateXY(imgMatrix);       float transX = 0;       float transY = 0;       if (isMoveX) {         endX = event.getRawX();         transX = endX - startX;         if ((XY[0] + transX) <= limitX1) {           transX = limitX1 - XY[0];         }         if ((XY[0] + transX) >= limitX2) {           transX = limitX2 - XY[0];         }       }       if (isMoveY) {         endY = event.getRawY();         transY = endY - startY;         if ((XY[1] + transY) <= limitY1) {           transY = limitY1 - XY[1];         }         if ((XY[1] + transY) >= limitY2) {           transY = limitY2 - XY[1];         }       }        imgMatrix.postTranslate(transX, transY);       startX = endX;       startY = endY;       this.setImageMatrix(imgMatrix);     } else if (mode == ZOOM && event.getPointerCount() > 1) {       endDistance = getDistance(event);       float dif = endDistance - startDistance;       if (Math.abs(endDistance - startDistance) > DOUBLE_POINT_DISTANCE) {         if (isBig) {           if (dif < 0) {             changeSize(0, 0);             mode = NONE;           }         } else if (dif > 0) {           float x = event.getX(0) / 2 + event.getX(1) / 2;           float y = event.getY(0) / 2 + event.getY(1) / 2;           changeSize(x, y);           mode = NONE;         }       }     }   }    /**    * 鼠标抬起事件    */   public void mouseUp() {     mode = NONE;   }    /**    * 图片放大缩小    *    * @param x    *      点击点X坐标    * @param y    *      点击点Y坐标    */   private void changeSize(float x, float y) {     if (isBig) {       // 如果处于最大状态,则还原       imgMatrix.reset();       imgMatrix.postScale(scale, scale);       imgMatrix.postTranslate(subX, subY);       isBig = false;     } else {       imgMatrix.postScale(bigScale, bigScale); // 在原有矩阵后乘放大倍数       float transX = -((bigScale - 1) * x);       float transY = -((bigScale - 1) * (y - topHeight)); // (bigScale-1)(y-statusBarHeight-subY)+2*subY;       float currentWidth = primaryW * scale * bigScale; // 放大后图片大小       float currentHeight = primaryH * scale * bigScale;       // 如果图片放大后超出屏幕范围处理       if (currentHeight > contentH) {         limitY1 = -(currentHeight - contentH); // 平移限制         limitY2 = 0;         isMoveY = true; // 允许在Y轴上拖动         float currentSubY = bigScale * subY; // 当前平移距离         // 平移后,内容区域上部有空白处理办法         if (-transY < currentSubY) {           transY = -currentSubY;         }         // 平移后,内容区域下部有空白处理办法         if (currentSubY + transY < limitY1) {           transY = -(currentHeight + currentSubY - contentH);         }       } else {         // 如果图片放大后没有超出屏幕范围处理,则不允许拖动         isMoveY = false;       }        if (currentWidth > contentW) {         limitX1 = -(currentWidth - contentW);         limitX2 = 0;         isMoveX = true;         float currentSubX = bigScale * subX;         if (-transX < currentSubX) {           transX = -currentSubX;         }         if (currentSubX + transX < limitX1) {           transX = -(currentWidth + currentSubX - contentW);         }       } else {         isMoveX = false;       }        imgMatrix.postTranslate(transX, transY);       isBig = true;     }      this.setImageMatrix(imgMatrix);     if (mCustomMethod != null) {       mCustomMethod.customMethod(isBig);     }   }    /**    * 获取变换矩阵中X轴偏移量和Y轴偏移量    *    * @param matrix    *      变换矩阵    * @return    */   private float[] getTranslateXY(Matrix matrix) {     float[] values = new float[9];     matrix.getValues(values);     float[] floats = new float[2];     floats[0] = values[Matrix.MTRANS_X];     floats[1] = values[Matrix.MTRANS_Y];     return floats;   }    /**    * 获取两点间的距离    *    * @param event    * @return    */   private float getDistance(MotionEvent event) {     float x = event.getX(0) - event.getX(1);     float y = event.getY(0) - event.getY(1);     return FloatMath.sqrt(x * x + y * y);   }    /**    * @author Administrator 用户自定义方法    */   public interface ICustomMethod {     public void customMethod(Boolean currentStatus);   } } 

 

ImageVewActivity 这个用于测试的Activity

import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import ejiang.boiler.ImageControl.ICustomMethod; import ejiang.boiler.R.id;  public class ImageViewActivity extends Activity {    @Override   protected void onCreate(Bundle savedInstanceState) {     // TODO Auto-generated method stub     super.onCreate(savedInstanceState);     setContentView(R.layout.common_image_view);     findView();   }    public void onWindowFocusChanged(boolean hasFocus) {     super.onWindowFocusChanged(hasFocus);     init();   }    ImageControl imgControl;   LinearLayout llTitle;   TextView tvTitle;    private void findView() {     imgControl = (ImageControl) findViewById(id.common_imageview_imageControl1);     llTitle = (LinearLayout) findViewById(id.common_imageview_llTitle);     tvTitle = (TextView) findViewById(id.common_imageview_title);   }    private void init() {     tvTitle.setText("图片测试");     // 这里可以为imgcontrol的图片路径动态赋值     // ............          Bitmap bmp;     if (imgControl.getDrawingCache() != null) {       bmp = Bitmap.createBitmap(imgControl.getDrawingCache());     } else {       bmp = ((BitmapDrawable) imgControl.getDrawable()).getBitmap();     }     Rect frame = new Rect();     getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);     int statusBarHeight = frame.top;     int screenW = this.getWindowManager().getDefaultDisplay().getWidth();     int screenH = this.getWindowManager().getDefaultDisplay().getHeight()         - statusBarHeight;     if (bmp != null) {       imgControl.imageInit(bmp, screenW, screenH, statusBarHeight,           new ICustomMethod() {                         @Override             public void customMethod(Boolean currentStatus) {               // 当图片处于放大或缩小状态时,控制标题是否显示               if (currentStatus) {                 llTitle.setVisibility(View.GONE);               } else {                 llTitle.setVisibility(View.VISIBLE);               }             }           });     }     else     {       Toast.makeText(ImageViewActivity.this, "图片加载失败,请稍候再试!", Toast.LENGTH_SHORT)           .show();     }    }    @Override   public boolean onTouchEvent(MotionEvent event) {     switch (event.getAction() & MotionEvent.ACTION_MASK) {     case MotionEvent.ACTION_DOWN:       imgControl.mouseDown(event);             break;      /**      * 非第一个点按下      */     case MotionEvent.ACTION_POINTER_DOWN:              imgControl.mousePointDown(event);            break;     case MotionEvent.ACTION_MOVE:         imgControl.mouseMove(event);              break;      case MotionEvent.ACTION_UP:       imgControl.mouseUp();       break;      }      return super.onTouchEvent(event);   } } 

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


common_image_view.xml  布局文件

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"   android:id="@+id/rl"   android:layout_width="fill_parent"   android:layout_height="fill_parent" >    <ejiang.boiler.ImageControl     android:id="@+id/common_imageview_imageControl1"     android:layout_width="fill_parent"     android:layout_height="fill_parent"     android:src="@drawable/ic_launcher" />    <LinearLayout     android:id="@+id/common_imageview_llTitle"     style="@style/reportTitle1"     android:layout_alignParentLeft="true"     android:layout_alignParentTop="true" >      <TextView       android:id="@+id/common_imageview_title"       style="@style/title2"       android:layout_width="fill_parent"       android:layout_height="wrap_content"       android:layout_weight="1"       android:text="报告" />   </LinearLayout>  </RelativeLayout> 

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表