天天看點

Android圖檔檢視支援輕按兩下放大縮小、多點觸摸

分類: Android 2012-10-10 17:37 1737人閱讀 評論(2) 收藏 舉報 android float layout constructor distance matrix

        前一段時間寫的,現在分享出來供大家參考。

        研究過圖庫的源碼,但着實太複雜,我都看不懂!也參考過網上的一些源碼,但很多功能都不全,都不是我想要的! 結合網上的代碼以及圖庫的部分源碼自己寫了一個類。

        未實作的功能——逐級放大,這個矩陣變換把我弄暈了,而且項目中也用不到,是以就沒再研究。

        該子產品主要實作了放大和原大兩個級别的縮放。

 功能有:

  1. 以觸摸點為中心放大(這個是網上其他的代碼沒有的)
  2. 邊界控制(這個是網上其他的代碼沒有的)
  3. 輕按兩下放大或縮小(主要考慮到電阻屏)
  4. 多點觸摸放大和縮小

        這個子產品已經通過了測試,并且使用者也使用有一段時間了,是屬于比較穩定的了。

下面貼上代碼及使用方法(沒有寫測試項目,大家見諒):

 ImageControl.cs   類似一個使用者自定義的ImageView控件。用法将在下面的代碼中貼出。

[java] view plain copy

  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. public class ImageControl extends ImageView {  
  9.     public ImageControl(Context context) {  
  10.         super(context);  
  11.         // TODO Auto-generated constructor stub  
  12.     }  
  13.     public ImageControl(Context context, AttributeSet attrs) {  
  14.         super(context, attrs);  
  15.         // TODO Auto-generated constructor stub  
  16.     }  
  17.     public ImageControl(Context context, AttributeSet attrs, int defStyle) {  
  18.         super(context, attrs, defStyle);  
  19.         // TODO Auto-generated constructor stub  
  20.     }  
  21.     // ImageView img;  
  22.     Matrix imgMatrix = null; // 定義圖檔的變換矩陣  
  23.     static final int DOUBLE_CLICK_TIME_SPACE = 300; // 輕按兩下時間間隔  
  24.     static final int DOUBLE_POINT_DISTANCE = 10; // 兩點放大兩點間最小間距  
  25.     static final int NONE = 0;  
  26.     static final int DRAG = 1; // 拖動操作  
  27.     static final int ZOOM = 2; // 放大縮小操作  
  28.     private int mode = NONE; // 目前模式  
  29.     float bigScale = 3f; // 預設放大倍數  
  30.     Boolean isBig = false; // 是否是放大狀态  
  31.     long lastClickTime = 0; // 單擊時間  
  32.     float startDistance; // 多點觸摸兩點距離  
  33.     float endDistance; // 多點觸摸兩點距離  
  34.     float topHeight; // 狀态欄高度和标題欄高度  
  35.     Bitmap primaryBitmap = null;  
  36.     float contentW; // 螢幕内容區寬度  
  37.     float contentH; // 螢幕内容區高度  
  38.     float primaryW; // 原圖寬度  
  39.     float primaryH; // 原圖高度  
  40.     float scale; // 适合螢幕縮放倍數  
  41.     Boolean isMoveX = true; // 是否允許在X軸拖動  
  42.     Boolean isMoveY = true; // 是否允許在Y軸拖動  
  43.     float startX;  
  44.     float startY;  
  45.     float endX;  
  46.     float endY;  
  47.     float subX;  
  48.     float subY;  
  49.     float limitX1;  
  50.     float limitX2;  
  51.     float limitY1;  
  52.     float limitY2;  
  53.     ICustomMethod mCustomMethod = null;  
  54.     public void imageInit(Bitmap bitmap, int contentW, int contentH,  
  55.             int topHeight, ICustomMethod iCustomMethod) {  
  56.         this.primaryBitmap = bitmap;  
  57.         this.contentW = contentW;  
  58.         this.contentH = contentH;  
  59.         this.topHeight = topHeight;  
  60.         mCustomMethod = iCustomMethod;  
  61.         primaryW = primaryBitmap.getWidth();  
  62.         primaryH = primaryBitmap.getHeight();  
  63.         float scaleX = (float) contentW / primaryW;  
  64.         float scaleY = (float) contentH / primaryH;  
  65.         scale = scaleX < scaleY ? scaleX : scaleY;  
  66.         if (scale < 1 && 1 / scale < bigScale) {  
  67.             bigScale = (float) (1 / scale + 0.5);  
  68.         }  
  69.         imgMatrix = new Matrix();  
  70.         subX = (contentW - primaryW * scale) / 2;  
  71.         subY = (contentH - primaryH * scale) / 2;  
  72.         this.setImageBitmap(primaryBitmap);  
  73.         this.setScaleType(ScaleType.MATRIX);  
  74.         imgMatrix.postScale(scale, scale);  
  75.         imgMatrix.postTranslate(subX, subY);  
  76.         this.setImageMatrix(imgMatrix);  
  77.     }  
  78.     public void mouseDown(MotionEvent event) {  
  79.         mode = NONE;  
  80.         startX = event.getRawX();  
  81.         startY = event.getRawY();  
  82.         if (event.getPointerCount() == 1) {  
  83.             // 如果兩次點選時間間隔小于一定值,則預設為輕按兩下事件  
  84.             if (event.getEventTime() - lastClickTime < DOUBLE_CLICK_TIME_SPACE) {  
  85.                 changeSize(startX, startY);  
  86.             } else if (isBig) {  
  87.                 mode = DRAG;  
  88.             }  
  89.         }  
  90.         lastClickTime = event.getEventTime();  
  91.     }  
  92.     public void mousePointDown(MotionEvent event) {  
  93.         startDistance = getDistance(event);  
  94.         if (startDistance > DOUBLE_POINT_DISTANCE) {  
  95.             mode = ZOOM;  
  96.         } else {  
  97.             mode = NONE;  
  98.         }  
  99.     }  
  100.     public void mouseMove(MotionEvent event) {  
  101.         if ((mode == DRAG) && (isMoveX || isMoveY)) {  
  102.             float[] XY = getTranslateXY(imgMatrix);  
  103.             float transX = 0;  
  104.             float transY = 0;  
  105.             if (isMoveX) {  
  106.                 endX = event.getRawX();  
  107.                 transX = endX - startX;  
  108.                 if ((XY[0] + transX) <= limitX1) {  
  109.                     transX = limitX1 - XY[0];  
  110.                 }  
  111.                 if ((XY[0] + transX) >= limitX2) {  
  112.                     transX = limitX2 - XY[0];  
  113.                 }  
  114.             }  
  115.             if (isMoveY) {  
  116.                 endY = event.getRawY();  
  117.                 transY = endY - startY;  
  118.                 if ((XY[1] + transY) <= limitY1) {  
  119.                     transY = limitY1 - XY[1];  
  120.                 }  
  121.                 if ((XY[1] + transY) >= limitY2) {  
  122.                     transY = limitY2 - XY[1];  
  123.                 }  
  124.             }  
  125.             imgMatrix.postTranslate(transX, transY);  
  126.             startX = endX;  
  127.             startY = endY;  
  128.             this.setImageMatrix(imgMatrix);  
  129.         } else if (mode == ZOOM && event.getPointerCount() > 1) {  
  130.             endDistance = getDistance(event);  
  131.             float dif = endDistance - startDistance;  
  132.             if (Math.abs(endDistance - startDistance) > DOUBLE_POINT_DISTANCE) {  
  133.                 if (isBig) {  
  134.                     if (dif < 0) {  
  135.                         changeSize(0, 0);  
  136.                         mode = NONE;  
  137.                     }  
  138.                 } else if (dif > 0) {  
  139.                     float x = event.getX(0) / 2 + event.getX(1) / 2;  
  140.                     float y = event.getY(0) / 2 + event.getY(1) / 2;  
  141.                     changeSize(x, y);  
  142.                     mode = NONE;  
  143.                 }  
  144.             }  
  145.         }  
  146.     }  
  147.     public void mouseUp() {  
  148.         mode = NONE;  
  149.     }  
  150.     private void changeSize(float x, float y) {  
  151.         if (isBig) {  
  152.             // 如果處于最大狀态,則還原  
  153.             imgMatrix.reset();  
  154.             imgMatrix.postScale(scale, scale);  
  155.             imgMatrix.postTranslate(subX, subY);  
  156.             isBig = false;  
  157.         } else {  
  158.             imgMatrix.postScale(bigScale, bigScale); // 在原有矩陣後乘放大倍數  
  159.             float transX = -((bigScale - 1) * x);  
  160.             float transY = -((bigScale - 1) * (y - topHeight)); // (bigScale-1)(y-statusBarHeight-subY)+2*subY;  
  161.             float currentWidth = primaryW * scale * bigScale; // 放大後圖檔大小  
  162.             float currentHeight = primaryH * scale * bigScale;  
  163.             // 如果圖檔放大後超出螢幕範圍處理  
  164.             if (currentHeight > contentH) {  
  165.                 limitY1 = -(currentHeight - contentH); // 平移限制  
  166.                 limitY2 = 0;  
  167.                 isMoveY = true; // 允許在Y軸上拖動  
  168.                 float currentSubY = bigScale * subY; // 目前平移距離  
  169.                 // 平移後,内容區域上部有空白處理辦法  
  170.                 if (-transY < currentSubY) {  
  171.                     transY = -currentSubY;  
  172.                 }  
  173.                 // 平移後,内容區域下部有空白處理辦法  
  174.                 if (currentSubY + transY < limitY1) {  
  175.                     transY = -(currentHeight + currentSubY - contentH);  
  176.                 }  
  177.             } else {  
  178.                 // 如果圖檔放大後沒有超出螢幕範圍處理,則不允許拖動  
  179.                 isMoveY = false;  
  180.             }  
  181.             if (currentWidth > contentW) {  
  182.                 limitX1 = -(currentWidth - contentW);  
  183.                 limitX2 = 0;  
  184.                 isMoveX = true;  
  185.                 float currentSubX = bigScale * subX;  
  186.                 if (-transX < currentSubX) {  
  187.                     transX = -currentSubX;  
  188.                 }  
  189.                 if (currentSubX + transX < limitX1) {  
  190.                     transX = -(currentWidth + currentSubX - contentW);  
  191.                 }  
  192.             } else {  
  193.                 isMoveX = false;  
  194.             }  
  195.             imgMatrix.postTranslate(transX, transY);  
  196.             isBig = true;  
  197.         }  
  198.         this.setImageMatrix(imgMatrix);  
  199.         if (mCustomMethod != null) {  
  200.             mCustomMethod.customMethod(isBig);  
  201.         }  
  202.     }  
  203.     private float[] getTranslateXY(Matrix matrix) {  
  204.         float[] values = new float[9];  
  205.         matrix.getValues(values);  
  206.         float[] floats = new float[2];  
  207.         floats[0] = values[Matrix.MTRANS_X];  
  208.         floats[1] = values[Matrix.MTRANS_Y];  
  209.         return floats;  
  210.     }  
  211.     private float getDistance(MotionEvent event) {  
  212.         float x = event.getX(0) - event.getX(1);  
  213.         float y = event.getY(0) - event.getY(1);  
  214.         return FloatMath.sqrt(x * x + y * y);  
  215.     }  
  216.     public interface ICustomMethod {  
  217.         public void customMethod(Boolean currentStatus);  
  218.     }  
  219. }  

ImageVewActivity.cs   這個用于測試的Activity

[java] view plain copy

  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. public class ImageViewActivity extends Activity {  
  14.     @Override  
  15.     protected void onCreate(Bundle savedInstanceState) {  
  16.         // TODO Auto-generated method stub  
  17.         super.onCreate(savedInstanceState);  
  18.         setContentView(R.layout.common_image_view);  
  19.         findView();  
  20.     }  
  21.     public void onWindowFocusChanged(boolean hasFocus) {  
  22.         super.onWindowFocusChanged(hasFocus);  
  23.         init();  
  24.     }  
  25.     ImageControl imgControl;  
  26.     LinearLayout llTitle;  
  27.     TextView tvTitle;  
  28.     private void findView() {  
  29.         imgControl = (ImageControl) findViewById(id.common_imageview_imageControl1);  
  30.         llTitle = (LinearLayout) findViewById(id.common_imageview_llTitle);  
  31.         tvTitle = (TextView) findViewById(id.common_imageview_title);  
  32.     }  
  33.     private void init() {  
  34.         tvTitle.setText("圖檔測試");  
  35.         // 這裡可以為imgcontrol的圖檔路徑動态指派  
  36.         // ............  
  37.         Bitmap bmp;  
  38.         if (imgControl.getDrawingCache() != null) {  
  39.             bmp = Bitmap.createBitmap(imgControl.getDrawingCache());  
  40.         } else {  
  41.             bmp = ((BitmapDrawable) imgControl.getDrawable()).getBitmap();  
  42.         }  
  43.         Rect frame = new Rect();  
  44.         getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);  
  45.         int statusBarHeight = frame.top;  
  46.         int screenW = this.getWindowManager().getDefaultDisplay().getWidth();  
  47.         int screenH = this.getWindowManager().getDefaultDisplay().getHeight()  
  48.                 - statusBarHeight;  
  49.         if (bmp != null) {  
  50.             imgControl.imageInit(bmp, screenW, screenH, statusBarHeight,  
  51.                     new ICustomMethod() {  
  52.                         @Override  
  53.                         public void customMethod(Boolean currentStatus) {  
  54.                             // 當圖檔處于放大或縮小狀态時,控制标題是否顯示  
  55.                             if (currentStatus) {  
  56.                                 llTitle.setVisibility(View.GONE);  
  57.                             } else {  
  58.                                 llTitle.setVisibility(View.VISIBLE);  
  59.                             }  
  60.                         }  
  61.                     });  
  62.         }  
  63.         else  
  64.         {  
  65.             Toast.makeText(ImageViewActivity.this, "圖檔加載失敗,請稍候再試!", Toast.LENGTH_SHORT)  
  66.                     .show();  
  67.         }  
  68.     }  
  69.     @Override  
  70.     public boolean onTouchEvent(MotionEvent event) {  
  71.         switch (event.getAction() & MotionEvent.ACTION_MASK) {  
  72.         case MotionEvent.ACTION_DOWN:  
  73.             imgControl.mouseDown(event);              
  74.             break;  
  75.         case MotionEvent.ACTION_POINTER_DOWN:  
  76.                 imgControl.mousePointDown(event);  
  77.             break;  
  78.         case MotionEvent.ACTION_MOVE:  
  79.                 imgControl.mouseMove(event);  
  80.             break;  
  81.         case MotionEvent.ACTION_UP:  
  82.             imgControl.mouseUp();  
  83.             break;  
  84.         }  
  85.         return super.onTouchEvent(event);  
  86.     }  
  87. }  

        在上面的代碼中,需要注意兩點。一Activity中要重寫onTouchEvent方法,将觸摸事件傳遞到ImageControl,這點類似于WPF中的路由事件機制。二初始化imgControl即imgControl.imageInit,注意其中的參數。最後一個參數類似于C#中的委托,我這裡使用接口來實作,在放大縮小的切換時要執行的操作都解除安裝這個方法中。

common_image_view.xml  布局檔案

[html] view plain copy

  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.     <ejiang.boiler.ImageControl  
  7.         android:id="@+id/common_imageview_imageControl1"  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="fill_parent"  
  10.         android:src="@drawable/ic_launcher" />  
  11.     <LinearLayout  
  12.         android:id="@+id/common_imageview_llTitle"  
  13.         style="@style/reportTitle1"  
  14.         android:layout_alignParentLeft="true"  
  15.         android:layout_alignParentTop="true" >  
  16.         <TextView  
  17.             android:id="@+id/common_imageview_title"  
  18.             style="@style/title2"  
  19.             android:layout_width="fill_parent"  
  20.             android:layout_height="wrap_content"  
  21.             android:layout_weight="1"  
  22.             android:text="報告" />  
  23.     </LinearLayout>  
  24. </RelativeLayout>  

        我學習android的時間也不長,是以有什麼纰漏或錯誤,歡迎大家指出!