Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android App中實現可以雙擊放大和縮小圖片功能的實例

Android App中實現可以雙擊放大和縮小圖片功能的實例

編輯:關於Android編程

先來看一個很簡單的核心圖片縮放方法:

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" 
     
    android:layout_alignParentLeft="true" 
    android:layout_alignParentTop="true" > 
 
    <TextView 
      android:id="@+id/common_imageview_title" 
       
      android:layout_width="fill_parent" 
      android:layout_height="wrap_content" 
      android:layout_weight="1" 
      android:text="報告" /> 
  </LinearLayout> 
 
</RelativeLayout> 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved