Android单张图片查看、单指移动、双指缩放、双击最大化或最小化

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;
	}

}


Android单张图片查看、单指移动、双指缩放、双击最大化或最小化

上一篇:ASP.NET Core 2.1 中异步使用Dapper总结


下一篇:CFS调度1-buddy