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

jopen 10年前

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