先來看一個很簡單的核心圖片縮放方法:
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>
新聞熱點
疑難解答