分类:
版权声明:本文为博主原创文章,未经博主允许不得转载。
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();
- <pre code_snippet_id="168679" snippet_file_name="blog_20140123_1_7635608" name="code" class="java"><pre code_snippet_id="168679" snippet_file_name="blog_20140123_1_7635608" name="code" class="java"> 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;
- }
- }
- }
- </pre>
- <pre></pre>
- <pre></pre>
- <p></p>
- <pre></pre>
- <pre></pre>
- <p><br>
- </p>
- <p><br>
- </p>
- <p></p>
- <p>测试代码如下:</p>
- <p><br>
- </p>
- <p>activity_main.xml文件:</p>
- <p></p>
- <pre code_snippet_id="168679" snippet_file_name="blog_20140123_2_8257273" name="code" class="html"><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>
- </pre><br>
- <p><br>
- </p>
- <p>MainActivity.java文件:</p>
- <p></p>
- <p></p>
- <pre code_snippet_id="168679" snippet_file_name="blog_20140123_3_2367928" name="code" class="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;
- }
- }
- </pre><br>
- <br>
- <p></p>
- </pre>