/* * The MIT License (MIT) * * Copyright (c) 2014-2015 Umeng, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.umeng.comm.ui.widgets; /** * 可缩放的ImageView,用于图片浏览 */ import android.content.Context; import android.content.DialogInterface.OnDismissListener; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Build.VERSION; import android.util.AttributeSet; import android.util.FloatMath; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.widget.ImageView; public class ScaleImageView extends ImageView { private Context mContext; private float MAX_SCALE = 8f; private Matrix mMatrix; private final float[] mMatrixValues = new float[9]; // display width height. private int mWidth; private int mHeight; private int mIntrinsicWidth; private int mIntrinsicHeight; private float mScale; private float mMinScale = 0.1f; private float mPrevDistance; private boolean isScaling; private int mPrevMoveX; private int mPrevMoveY; private int mLastX; private int mLastY; private GestureDetector mDetector; private float mSlop; private android.content.DialogInterface.OnDismissListener mListener; private float mDefaultScale = 0f; public ScaleImageView(Context context, AttributeSet attr) { super(context, attr); this.mContext = context; initialize(); } public ScaleImageView(Context context) { super(context); this.mContext = context; initialize(); } @Override public void setImageBitmap(Bitmap bm) { super.setImageBitmap(bm); this.initialize(); } @Override public void setImageResource(int resId) { super.setImageResource(resId); this.initialize(); } private void initialize() { this.setScaleType(ScaleType.MATRIX); this.mMatrix = new Matrix(); Drawable d = getDrawable(); if (d != null) { mIntrinsicWidth = d.getIntrinsicWidth(); mIntrinsicHeight = d.getIntrinsicHeight(); } // 双击的情况。首先放大到图片的最大比例8,再双击则显示原图大小 mDetector = new GestureDetector(mContext, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDoubleTap(MotionEvent e) { maxZoomTo((int) e.getX(), (int) e.getY()); cutting(); return super.onDoubleTap(e); } }); mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); Log.e(VIEW_LOG_TAG, "### 图片宽度 : " + mIntrinsicWidth + ", height : " + mIntrinsicHeight); } public void updateScale() { mWidth = getWidth(); mHeight = getHeight(); mMatrix.reset(); mScale = (float) mWidth / (float) mIntrinsicWidth; if (mScale * mIntrinsicHeight > mHeight) { mScale = (float) mHeight / (float) mIntrinsicHeight; } zoomTo(mScale, mWidth / 2, mHeight / 2); cutting(); } @Override protected boolean setFrame(int l, int t, int r, int b) { mWidth = r - l; mHeight = b - t; mMatrix.reset(); int r_norm = r - l; mScale = (float) r_norm / (float) mIntrinsicWidth; if (mScale * mIntrinsicHeight > mHeight) { mScale = (float) mHeight / (float) mIntrinsicHeight; } zoomTo(mScale, mWidth / 2, mHeight / 2); cutting(); return super.setFrame(l, t, r, b); } protected float getValue(Matrix matrix, int whichValue) { matrix.getValues(mMatrixValues); return mMatrixValues[whichValue]; } protected float getScale() { return getValue(mMatrix, Matrix.MSCALE_X); } public float getTranslateX() { return getValue(mMatrix, Matrix.MTRANS_X); } protected float getTranslateY() { return getValue(mMatrix, Matrix.MTRANS_Y); } protected void maxZoomTo(int x, int y) { float scale = 0; if (MAX_SCALE != getScale() && (Math.abs(getScale() - MAX_SCALE)) > 0.1f) { scale = MAX_SCALE / getScale(); } else { scale = mMinScale / getScale(); } zoomTo(scale, x, y); } public void reset() { mMatrix.reset(); mScale = mMinScale; setImageMatrix(mMatrix); } public void zoomTo(float scale, int x, int y) { if (getScale() * scale < mMinScale) { return; } if (scale >= 1 && getScale() * scale > MAX_SCALE) { return; } mMatrix.postScale(scale, scale); // move to center mMatrix.postTranslate(-(mWidth * scale - mWidth) / 2, -(mHeight * scale - mHeight) / 2); // move x and y distance mMatrix.postTranslate(-(x - (mWidth / 2)) * scale, 0); mMatrix.postTranslate(0, -(y - (mHeight / 2)) * scale); setImageMatrix(mMatrix); } public void cutting() { int width = (int) (mIntrinsicWidth * getScale()); int height = (int) (mIntrinsicHeight * getScale()); if (getTranslateX() < -(width - mWidth)) { mMatrix.postTranslate(-(getTranslateX() + width - mWidth), 0); } if (getTranslateX() > 0) { mMatrix.postTranslate(-getTranslateX(), 0); } if (getTranslateY() < -(height - mHeight)) { mMatrix.postTranslate(0, -(getTranslateY() + height - mHeight)); } if (getTranslateY() > 0) { mMatrix.postTranslate(0, -getTranslateY()); } if (width < mWidth) { mMatrix.postTranslate((mWidth - width) / 2, 0); } if (height < mHeight) { mMatrix.postTranslate(0, (mHeight - height) / 2); } setImageMatrix(mMatrix); } private float distance(float x0, float x1, float y0, float y1) { float x = x0 - x1; float y = y0 - y1; return FloatMath.sqrt(x * x + y * y); } private float dispDistance() { return FloatMath.sqrt(mWidth * mWidth + mHeight * mHeight); } @Override public boolean dispatchTouchEvent(MotionEvent event) { if (isGingerbread() || event.getAction() != MotionEvent.ACTION_MOVE) { return super.dispatchTouchEvent(event); } if (mDefaultScale == 0f) { mDefaultScale = mScale; } if (getScale() - 1.0f >= 0.01f && !checkEdge(event)) {// 放大且没有移动到边缘,此时事件交给子控件处理 getParent().requestDisallowInterceptTouchEvent(true); } else { getParent().requestDisallowInterceptTouchEvent(false);// 交给ViewPager处理 } return super.dispatchTouchEvent(event); } @SuppressWarnings("deprecation") @Override public boolean onTouchEvent(MotionEvent event) { if (mDetector.onTouchEvent(event)) { return true; } int touchCount = event.getPointerCount(); switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_1_DOWN: case MotionEvent.ACTION_POINTER_2_DOWN: if (touchCount >= 2) { float distance = distance(event.getX(0), event.getX(1), event.getY(0), event.getY(1)); mPrevDistance = distance; isScaling = true; } else { mPrevMoveX = (int) event.getX(); mPrevMoveY = (int) event.getY(); mLastX = mPrevMoveX; mLastY = mPrevMoveY; } case MotionEvent.ACTION_MOVE: if (touchCount >= 2 && isScaling) { float dist = distance(event.getX(0), event.getX(1), event.getY(0), event.getY(1)); // if ( Math.abs(dist - mPrevDistance) < mSlop ) { // Log.d("", "######## move dis " + dist + " " + mSlop); // return false; // } float scale = (dist - mPrevDistance) / dispDistance(); mPrevDistance = dist; scale += 1; scale = scale * scale; zoomTo(scale, mWidth / 2, mHeight / 2); cutting(); } else if (!isScaling) { int distanceX = mPrevMoveX - (int) event.getX(); int distanceY = mPrevMoveY - (int) event.getY(); mPrevMoveX = (int) event.getX(); mPrevMoveY = (int) event.getY(); mMatrix.postTranslate(-distanceX, -distanceY); cutting(); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_POINTER_2_UP: if (event.getPointerCount() <= 1) { isScaling = false; checkExecuteCallback(event); } else { if (mDefaultScale == 0f) { mDefaultScale = mScale; } } mPrevDistance = 0; mPrevMoveX = 0; mPrevMoveY = 0; mLastX = 0; mLastY = 0; break; } return true; } /** * 检查是否执行回调</br> * * @param event */ private void checkExecuteCallback(MotionEvent event) { int delta = (int) distance(mLastX, event.getX(), mLastY, event.getY()); if (delta < mSlop && mListener != null) { // 此时仅仅是点击,执行回调,dismiss dialog mListener.onDismiss(null); } } /** * 检查是否滑动到放大的边缘</br> * * @param event * @return */ private boolean checkEdge(MotionEvent event) { if (isGingerbread()) { return super.dispatchTouchEvent(event); } mapDisplayRect(); // Log.d("", "#############----" +mDisplayRect.left + " " + // mDisplayRect.right + " " + getWidth() + " " + mSlop); // Log.d("", "#############----" +getScale() + " " + mDefaultScale); // 已经滑动到边缘 if (Math.abs(mDisplayRect.left) <= mSlop || (Math.abs(mDisplayRect.left) > mSlop && getScale() - mDefaultScale <= 0.5f) || Math.abs(mDisplayRect.right - getWidth()) < mSlop || (Math.abs(mDisplayRect.right - getWidth()) >= mSlop && getScale() - mDefaultScale <= 0.5f)) { return true; } return false; } private final RectF mDisplayRect = new RectF(); /** * 将图片的尺寸矩阵map成RectF</br> * * @return */ private RectF mapDisplayRect() { Drawable d = getDrawable(); if (null != d) { mDisplayRect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); getImageMatrix().mapRect(mDisplayRect); return mDisplayRect; } return null; } public void setOndismissListener(OnDismissListener listener) { mListener = listener; } private boolean isGingerbread() { if (VERSION.SDK_INT == Build.VERSION_CODES.GINGERBREAD) { return true; } return false; } }