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_CLICK_TIME_SPACE) { changeSize(event.getX(), event.getY()); } lastClickTime = event.getEventTime(); break; case MotionEvent.ACTION_POINTER_DOWN: oldDist = spacing(event); if (oldDist > 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 < distance(event, down)) { preMatrix.set(savedMatrix); preMatrix.postTranslate(event.getX() - down.x, 0); if (event.getX() > 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] < widthScreen - 1f) { preMatrix.postTranslate((widthScreen - (x[1] - x[0])) / 2 - x[0], 0); matrix.set(preMatrix); invalidate(); } if (y[2] - y[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] < heightScreen - 1f) { preMatrix.postTranslate(0, (heightScreen - (y[2] - y[0])) / 2 - y[0]); matrix.set(preMatrix); invalidate(); } } private void getFourPoint(float[] x, float[] y) { float[] f = new float[9]; preMatrix.getValues(f); // 图片4个顶点的坐标 x[0] = f[Matrix.MSCALE_X] * 0 + f[Matrix.MSKEW_X] * 0 + f[Matrix.MTRANS_X]; y[0] = f[Matrix.MSKEW_Y] * 0 + f[Matrix.MSCALE_Y] * 0 + f[Matrix.MTRANS_Y]; x[1] = f[Matrix.MSCALE_X] * touchImg.getWidth() + f[Matrix.MSKEW_X] * 0 + f[Matrix.MTRANS_X]; y[1] = f[Matrix.MSKEW_Y] * touchImg.getWidth() + f[Matrix.MSCALE_Y] * 0 + f[Matrix.MTRANS_Y]; x[2] = f[Matrix.MSCALE_X] * 0 + f[Matrix.MSKEW_X] * touchImg.getHeight() + f[Matrix.MTRANS_X]; y[2] = f[Matrix.MSKEW_Y] * 0 + f[Matrix.MSCALE_Y] * touchImg.getHeight() + f[Matrix.MTRANS_Y]; x[3] = f[Matrix.MSCALE_X] * touchImg.getWidth() + f[Matrix.MSKEW_X] * touchImg.getHeight() + f[Matrix.MTRANS_X]; y[3] = f[Matrix.MSKEW_Y] * touchImg.getWidth() + f[Matrix.MSCALE_Y] * touchImg.getHeight() + f[Matrix.MTRANS_Y]; } private final static int DRAG_LEFT = 0; private final static int DRAG_RIGHT = 1; private final static int DRAG_TOP = 2; private final static int DRAG_DOWN = 3; private boolean canDrag(final int direction) { float[] x = new float[4]; float[] y = new float[4]; getFourPoint(x, y); // 出界判断 if ((x[0] > 0 || x[2] > 0 || x[1] < widthScreen || x[3] < widthScreen) && (y[0] > 0 || y[1] > 0 || y[2] < heightScreen || y[3] < heightScreen)) { return false; } if (DRAG_LEFT == direction) { // 左移出界判断 if (x[1] < widthScreen || x[3] < widthScreen) { return false; } } else if (DRAG_RIGHT == direction) { // 右移出界判断 if (x[0] > 0 || x[2] > 0) { return false; } } else if (DRAG_TOP == direction) { // 上移出界判断 if (y[2] < heightScreen || y[3] < heightScreen) { return false; } } else if (DRAG_DOWN == direction) { // 下移出界判断 if (y[0] > 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 * defaultScale - 1 || 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文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/layout_id" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <com.example.test.TouchImageView android:id="@+id/img_id" android:layout_width="400dip" android:layout_height="800dip" android:src="@drawable/banner" android:scaleType="fitCenter" /> </LinearLayout>
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; } }