Android平臺上查看單張圖片時,通常情況下需要實現圖片查看、單指移動、雙指縮放、雙擊最大化或最小化功能。
目前網絡上的實現方式,都沒有將此功能封裝為類,零落在類和xml文件中,代碼難以閱讀,功能難以復用。
為此,我專門寫瞭一個類做此功能。此類唯一的缺點是沒有實現回彈動畫。不說廢話瞭,上代碼。
代碼如下:
package com.example.test; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.drawable.BitmapDrawable; import android.util.AttributeSet; import android.util.FloatMath; import android.util.Log; import android.view.MotionEvent; import android.widget.ImageView; public class TouchImageView extends ImageView { private PointF down = new PointF(); private PointF mid = new PointF();private float oldDist = 1f; private Matrix matrix = new Matrix(); private Matrix preMatrix = new Matrix(); private Matrix savedMatrix = new Matrix(); private static final int NONE = 0; private static final int DRAG = 1; private static final int ZOOM = 2; private int mode = NONE; private boolean isBig = false; private int widthScreen; private int heightScreen; private int touchImgWidth; private int touchImgHeight; private float defaultScale; private long lastClickTime = 0; private Bitmap touchImg = null; private static final int DOUBLE_CLICK_TIME_SPACE = 300; private static final int DOUBLE_POINT_DISTANCE = 10; private static float MAX_SCALE = 3.0f; public TouchImageView(Context context) { super(context); } public TouchImageView(Context context, AttributeSet attrs) { super(context, attrs); } public TouchImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void initImageView(int screenWidth, int screenHeight) { widthScreen = screenWidth; heightScreen = screenHeight; touchImg = ((BitmapDrawable) getDrawable()).getBitmap(); touchImgWidth = touchImg.getWidth(); touchImgHeight = touchImg.getHeight(); float scaleX = (float) widthScreen / touchImgWidth; float scaleY = (float) heightScreen / touchImgHeight; defaultScale = scaleX < scaleY ? scaleX : scaleY; float subX = (widthScreen - touchImgWidth * defaultScale) / 2; float subY = (heightScreen - touchImgHeight * defaultScale) / 2; setScaleType(ScaleType.MATRIX); preMatrix.reset(); preMatrix.postScale(defaultScale, defaultScale); preMatrix.postTranslate(subX, subY); matrix.set(preMatrix); invalidate(); } @Override protected void onDraw(Canvas canvas) { if (null != touchImg) { canvas.save(); canvas.drawBitmap(touchImg, matrix, null); canvas.restore(); } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: mode = DRAG; down.x = event.getX(); down.y = event.getY(); savedMatrix.set(matrix); if (event.getEventTime() - lastClickTime DOUBLE_POINT_DISTANCE) { mode = ZOOM; // oldRotation = rotation(event); savedMatrix.set(matrix); midPoint(mid, event); } break; case MotionEvent.ACTION_MOVE: if (mode == ZOOM) { float newDist = spacing(event); float scale = newDist / oldDist; if (scale > 1.01 || scale < 0.99) { preMatrix.set(savedMatrix); preMatrix.postScale(scale, scale, mid.x, mid.y);// 縮放 if (canZoom()) { matrix.set(preMatrix); invalidate(); } } } else if (mode == DRAG) { if (1.0f down.x) { if (canDrag(DRAG_RIGHT)) { savedMatrix.set(preMatrix); } else { preMatrix.set(savedMatrix); } } else { if (canDrag(DRAG_LEFT)) { savedMatrix.set(preMatrix); } else { preMatrix.set(savedMatrix); } } preMatrix.postTranslate(0, event.getY() - down.y); if (event.getY() > down.y) { if (canDrag(DRAG_DOWN)) { savedMatrix.set(preMatrix); } else { preMatrix.set(savedMatrix); } } else { if (canDrag(DRAG_TOP)) { savedMatrix.set(preMatrix); } else { preMatrix.set(savedMatrix); } } matrix.set(preMatrix); invalidate(); down.x = event.getX(); down.y = event.getY(); savedMatrix.set(matrix); } } break; case MotionEvent.ACTION_UP: mode = NONE; springback(); break; case MotionEvent.ACTION_POINTER_UP: mode = NONE; break; } return true; } private void springback() { preMatrix.set(matrix); float[] x = new float[4]; float[] y = new float[4]; getFourPoint(x, y); if (x[1] - x[0] > widthScreen) { if (x[0] > 0) { preMatrix.postTranslate(-x[0], 0); matrix.set(preMatrix); invalidate(); } else if (x[1] < widthScreen) { preMatrix.postTranslate(widthScreen - x[1], 0); matrix.set(preMatrix); invalidate(); } } else if (x[1] - x[0] heightScreen) { if (y[0] > 0) { preMatrix.postTranslate(0, -y[0]); matrix.set(preMatrix); invalidate(); } else if (y[2] < heightScreen) { preMatrix.postTranslate(0, heightScreen - y[2]); matrix.set(preMatrix); invalidate(); } } else if (y[2] - y[0] 0 || x[2] > 0 || x[1] < widthScreen || x[3] 0 || y[1] > 0 || y[2] < heightScreen || y[3] < heightScreen)) { return false; } if (DRAG_LEFT == direction) { // 左移出界判斷 if (x[1] < widthScreen || x[3] 0 || x[2] > 0) { return false; } } else if (DRAG_TOP == direction) { // 上移出界判斷 if (y[2] < heightScreen || y[3] 0 || y[1] > 0) { return false; } } else { return false; } return true; } private boolean canZoom() { float[] x = new float[4]; float[] y = new float[4]; getFourPoint(x, y); // 圖片現寬度 double width = Math.sqrt((x[0] - x[1]) * (x[0] - x[1]) + (y[0] - y[1]) * (y[0] - y[1])); double height = Math.sqrt((x[0] - x[2]) * (x[0] - x[2]) + (y[0] - y[2]) * (y[0] - y[2])); // 縮放比率判斷 if (width touchImgWidth * MAX_SCALE + 1) { return false; } // 出界判斷 if (width < widthScreen && height < heightScreen) { return false; } return true; } // 觸碰兩點間距離 private static float spacing(MotionEvent event) { float x = event.getX(0) - event.getX(1); if (x < 0) { x = -x; } float y = event.getY(0) - event.getY(1); if (y < 0) { y = -y; } return FloatMath.sqrt(x * x + y * y); } // 取手勢中心點 private static void midPoint(PointF point, MotionEvent event) { float x = event.getX(0) + event.getX(1); float y = event.getY(0) + event.getY(1); point.set(x / 2, y / 2); } // 取兩點之間的距離 private static float distance(MotionEvent point2, PointF point1) { float x = point1.x - point2.getX(); if (x < 0) { x = -x; } float y = point1.y - point2.getY(); if (y < 0) { y = -y; } return FloatMath.sqrt(x * x + y * y); } private void changeSize(float x, float y) { if (isBig) { float subX = (widthScreen - touchImgWidth * defaultScale) / 2; float subY = (heightScreen - touchImgHeight * defaultScale) / 2; preMatrix.reset(); preMatrix.postScale(defaultScale, defaultScale); preMatrix.postTranslate(subX, subY); matrix.set(preMatrix); invalidate(); isBig = false; } else { float transX = (widthScreen - touchImgWidth * MAX_SCALE) / 2; float transY = (heightScreen - touchImgHeight * MAX_SCALE) / 2; preMatrix.reset(); preMatrix.postScale(MAX_SCALE, MAX_SCALE); preMatrix.postTranslate(transX, transY); matrix.set(preMatrix); invalidate(); isBig = true; } } }
測試代碼如下:
activity_main.xml文件:
MainActivity.java文件:
package com.example.test; import android.app.Activity; import android.os.Bundle; import android.util.DisplayMetrics; import android.view.Menu; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.LinearLayout; public class MainActivity extends Activity { private TouchImageView imgView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); TouchImageView imgView = (TouchImageView) findViewById(R.id.img_id); imgView.initImageView(dm.widthPixels, dm.heightPixels - 80); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }