精华内容
下载资源
问答
  • 显示图片时,希望支持手势缩放图片. 解决方案 添加依赖 implementation 'com.github.chrisbanes:PhotoView:2.3.0' 使用 <com.github.chrisbanes.photoview.PhotoView android:layout_width="match_parent" ...

    背景

    显示图片时,希望支持手势缩放图片.

    解决方案

    1. 添加依赖
        implementation 'com.github.chrisbanes:PhotoView:2.3.0'
    
    1. 使用
    <com.github.chrisbanes.photoview.PhotoView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@mipmap/ic_launcher"/>
    
    展开全文
  • 安卓图片手势缩放-源码

    千次阅读 2019-01-02 16:11:35
    以下是自定义支持手势缩放的zoomImageview,代码拿过去直接可以用 public class ZoomImageView extends ImageView implements OnScaleGestureListener, OnTouchListener, ViewTreeObserver....

    以下是自定义支持手势缩放的zoomImageview,代码拿过去直接可以用

    public class ZoomImageView extends ImageView implements OnScaleGestureListener,
    		OnTouchListener, ViewTreeObserver.OnGlobalLayoutListener
    
    {
    	private static final String TAG = ZoomImageView.class.getSimpleName();
    	public static final float SCALE_MAX = 4.0f;
    	private static final float SCALE_MID = 2.0f;
    
    	/**
    	 * 初始化时的缩放比例,如果图片宽或高大于屏幕,此值将小于0
    	 */
    	private float initScale = 1.0f;
    	private boolean once = true;
    
    	/**
    	 * 用于存放矩阵的9个值
    	 */
    	private final float[] matrixValues = new float[9];
    
    	/**
    	 * 缩放的手势检测
    	 */
    	private ScaleGestureDetector mScaleGestureDetector = null;
    	private final Matrix mScaleMatrix = new Matrix();
    
    	/**
    	 * 用于双击检测
    	 */
    	private GestureDetector mGestureDetector;
    	private boolean isAutoScale;
    
    	private int mTouchSlop;
    
    	private float mLastX;
    	private float mLastY;
    
    	private boolean isCanDrag;
    	private int lastPointerCount;
    
    	private boolean isCheckTopAndBottom = true;
    	private boolean isCheckLeftAndRight = true;
    
    	public ZoomImageView(Context context)
    	{
    		this(context, null);
    	}
    
    	public ZoomImageView(Context context, AttributeSet attrs)
    	{
    		super(context, attrs);
    		super.setScaleType(ScaleType.MATRIX);
    		mGestureDetector = new GestureDetector(context,
    				new SimpleOnGestureListener()
    				{
    					@Override
    					public boolean onDoubleTap(MotionEvent e)
    					{
    						if (isAutoScale == true)
    							return true;
    
    						float x = e.getX();
    						float y = e.getY();
    						Log.e("DoubleTap", getScale() + " , " + initScale);
    						if (getScale() < SCALE_MID)
    						{
    							ZoomImageView.this.postDelayed(
    									new AutoScaleRunnable(SCALE_MID, x, y), 16);
    							isAutoScale = true;
    						} else if (getScale() >= SCALE_MID
    								&& getScale() < SCALE_MAX)
    						{
    							ZoomImageView.this.postDelayed(
    									new AutoScaleRunnable(SCALE_MAX, x, y), 16);
    							isAutoScale = true;
    						} else
    						{
    							ZoomImageView.this.postDelayed(
    									new AutoScaleRunnable(initScale, x, y), 16);
    							isAutoScale = true;
    						}
    
    						return true;
    					}
    				});
    		mScaleGestureDetector = new ScaleGestureDetector(context, this);
    		this.setOnTouchListener(this);
    	}
    
    	/**
    	 * 自动缩放的任务
    	 * 
    	 * @author zhy
    	 * 
    	 */
    	private class AutoScaleRunnable implements Runnable
    	{
    		static final float BIGGER = 1.07f;
    		static final float SMALLER = 0.93f;
    		private float mTargetScale;
    		private float tmpScale;
    
    		/**
    		 * 缩放的中心
    		 */
    		private float x;
    		private float y;
    
    		/**
    		 * 传入目标缩放值,根据目标值与当前值,判断应该放大还是缩小
    		 * 
    		 * @param targetScale
    		 */
    		public AutoScaleRunnable(float targetScale, float x, float y)
    		{
    			this.mTargetScale = targetScale;
    			this.x = x;
    			this.y = y;
    			if (getScale() < mTargetScale)
    			{
    				tmpScale = BIGGER;
    			} else
    			{
    				tmpScale = SMALLER;
    			}
    
    		}
    
    		@Override
    		public void run()
    		{
    			// 进行缩放
    			mScaleMatrix.postScale(tmpScale, tmpScale, x, y);
    			checkBorderAndCenterWhenScale();
    			setImageMatrix(mScaleMatrix);
    
    			final float currentScale = getScale();
    			// 如果值在合法范围内,继续缩放
    			if (((tmpScale > 1f) && (currentScale < mTargetScale))
    					|| ((tmpScale < 1f) && (mTargetScale < currentScale)))
    			{
    				ZoomImageView.this.postDelayed(this, 16);
    			} else
    			// 设置为目标的缩放比例
    			{
    				final float deltaScale = mTargetScale / currentScale;
    				mScaleMatrix.postScale(deltaScale, deltaScale, x, y);
    				checkBorderAndCenterWhenScale();
    				setImageMatrix(mScaleMatrix);
    				isAutoScale = false;
    			}
    
    		}
    	}
    
    	@SuppressLint("NewApi")
    	@Override
    	public boolean onScale(ScaleGestureDetector detector)
    	{
    		float scale = getScale();
    		float scaleFactor = detector.getScaleFactor();
    
    		if (getDrawable() == null)
    			return true;
    
    		/**
    		 * 缩放的范围控制
    		 */
    		if ((scale < SCALE_MAX && scaleFactor > 1.0f)
    				|| (scale > initScale && scaleFactor < 1.0f))
    		{
    			/**
    			 * 最大值最小值判断
    			 */
    			if (scaleFactor * scale < initScale)
    			{
    				scaleFactor = initScale / scale;
    			}
    			if (scaleFactor * scale > SCALE_MAX)
    			{
    				scaleFactor = SCALE_MAX / scale;
    			}
    			/**
    			 * 设置缩放比例
    			 */
    			mScaleMatrix.postScale(scaleFactor, scaleFactor,
    					detector.getFocusX(), detector.getFocusY());
    			checkBorderAndCenterWhenScale();
    			setImageMatrix(mScaleMatrix);
    		}
    		return true;
    
    	}
    
    	/**
    	 * 在缩放时,进行图片显示范围的控制
    	 */
    	private void checkBorderAndCenterWhenScale()
    	{
    
    		RectF rect = getMatrixRectF();
    		float deltaX = 0;
    		float deltaY = 0;
    
    		int width = getWidth();
    		int height = getHeight();
    
    		// 如果宽或高大于屏幕,则控制范围
    		if (rect.width() >= width)
    		{
    			if (rect.left > 0)
    			{
    				deltaX = -rect.left;
    			}
    			if (rect.right < width)
    			{
    				deltaX = width - rect.right;
    			}
    		}
    		if (rect.height() >= height)
    		{
    			if (rect.top > 0)
    			{
    				deltaY = -rect.top;
    			}
    			if (rect.bottom < height)
    			{
    				deltaY = height - rect.bottom;
    			}
    		}
    		// 如果宽或高小于屏幕,则让其居中
    		if (rect.width() < width)
    		{
    			deltaX = width * 0.5f - rect.right + 0.5f * rect.width();
    		}
    		if (rect.height() < height)
    		{
    			deltaY = height * 0.5f - rect.bottom + 0.5f * rect.height();
    		}
    		Log.e(TAG, "deltaX = " + deltaX + " , deltaY = " + deltaY);
    
    		mScaleMatrix.postTranslate(deltaX, deltaY);
    
    	}
    
    	/**
    	 * 根据当前图片的Matrix获得图片的范围
    	 * 
    	 * @return
    	 */
    	private RectF getMatrixRectF()
    	{
    		Matrix matrix = mScaleMatrix;
    		RectF rect = new RectF();
    		Drawable d = getDrawable();
    		if (null != d)
    		{
    			rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
    			matrix.mapRect(rect);
    		}
    		return rect;
    	}
    
    	@Override
    	public boolean onScaleBegin(ScaleGestureDetector detector)
    	{
    		return true;
    	}
    
    	@Override
    	public void onScaleEnd(ScaleGestureDetector detector)
    	{
    	}
    
    	@Override
    	public boolean onTouch(View v, MotionEvent event)
    	{
    
    		if (mGestureDetector.onTouchEvent(event))
    			return true;
    		mScaleGestureDetector.onTouchEvent(event);
    
    		float x = 0, y = 0;
    		// 拿到触摸点的个数
    		final int pointerCount = event.getPointerCount();
    		// 得到多个触摸点的x与y均值
    		for (int i = 0; i < pointerCount; i++)
    		{
    			x += event.getX(i);
    			y += event.getY(i);
    		}
    		x = x / pointerCount;
    		y = y / pointerCount;
    
    		/**
    		 * 每当触摸点发生变化时,重置mLasX , mLastY
    		 */
    		if (pointerCount != lastPointerCount)
    		{
    			isCanDrag = false;
    			mLastX = x;
    			mLastY = y;
    		}
    
    		lastPointerCount = pointerCount;
    		RectF rectF = getMatrixRectF();
    		switch (event.getAction())
    		{
    		case MotionEvent.ACTION_DOWN:
    			if (rectF.width() > getWidth() || rectF.height() > getHeight())
    			{
    				getParent().requestDisallowInterceptTouchEvent(true);
    			}
    			break;
    		case MotionEvent.ACTION_MOVE:
    			if (rectF.width() > getWidth() || rectF.height() > getHeight())
    			{
    				getParent().requestDisallowInterceptTouchEvent(true);
    			}
    			Log.e(TAG, "ACTION_MOVE");
    			float dx = x - mLastX;
    			float dy = y - mLastY;
    
    			if (!isCanDrag)
    			{
    				isCanDrag = isCanDrag(dx, dy);
    			}
    			if (isCanDrag)
    			{
    
    				if (getDrawable() != null)
    				{
    					// if (getMatrixRectF().left == 0 && dx > 0)
    					// {
    					// getParent().requestDisallowInterceptTouchEvent(false);
    					// }
    					//
    					// if (getMatrixRectF().right == getWidth() && dx < 0)
    					// {
    					// getParent().requestDisallowInterceptTouchEvent(false);
    					// }
    					isCheckLeftAndRight = isCheckTopAndBottom = true;
    					// 如果宽度小于屏幕宽度,则禁止左右移动
    					if (rectF.width() < getWidth())
    					{
    						dx = 0;
    						isCheckLeftAndRight = false;
    					}
    					// 如果高度小雨屏幕高度,则禁止上下移动
    					if (rectF.height() < getHeight())
    					{
    						dy = 0;
    						isCheckTopAndBottom = false;
    					}
    					
    
    					mScaleMatrix.postTranslate(dx, dy);
    					checkMatrixBounds();
    					setImageMatrix(mScaleMatrix);
    				}
    			}
    			mLastX = x;
    			mLastY = y;
    			break;
    
    		case MotionEvent.ACTION_UP:
    		case MotionEvent.ACTION_CANCEL:
    			Log.e(TAG, "ACTION_UP");
    			lastPointerCount = 0;
    			break;
    		}
    
    		return true;
    	}
    
    	/**
    	 * 获得当前的缩放比例
    	 * 
    	 * @return
    	 */
    	public final float getScale()
    	{
    		mScaleMatrix.getValues(matrixValues);
    		return matrixValues[Matrix.MSCALE_X];
    	}
    
    	@Override
    	protected void onAttachedToWindow()
    	{
    		super.onAttachedToWindow();
    		getViewTreeObserver().addOnGlobalLayoutListener(this);
    	}
    
    	@SuppressWarnings("deprecation")
    	@Override
    	protected void onDetachedFromWindow()
    	{
    		super.onDetachedFromWindow();
    		getViewTreeObserver().removeGlobalOnLayoutListener(this);
    	}
    
    	@Override
    	public void onGlobalLayout()
    	{
    		if (once)
    		{
    			Drawable d = getDrawable();
    			if (d == null)
    				return;
    			Log.e(TAG, d.getIntrinsicWidth() + " , " + d.getIntrinsicHeight());
    			int width = getWidth();
    			int height = getHeight();
    			// 拿到图片的宽和高
    			int dw = d.getIntrinsicWidth();
    			int dh = d.getIntrinsicHeight();
    			float scale = 1.0f;
    			// 如果图片的宽或者高大于屏幕,则缩放至屏幕的宽或者高
    			if (dw > width && dh <= height)
    			{
    				scale = width * 1.0f / dw;
    			}
    			if (dh > height && dw <= width)
    			{
    				scale = height * 1.0f / dh;
    			}
    			// 如果宽和高都大于屏幕,则让其按按比例适应屏幕大小
    			if (dw > width && dh > height)
    			{
    				scale = Math.min(width * 1.0f / dw, height * 1.0f / dh);
    			}
    			initScale = scale;
    
    			Log.e(TAG, "initScale = " + initScale);
    			mScaleMatrix.postTranslate((width - dw) / 2, (height - dh) / 2);
    			mScaleMatrix.postScale(scale, scale, getWidth() / 2,
    					getHeight() / 2);
    			// 图片移动至屏幕中心
    			setImageMatrix(mScaleMatrix);
    			once = false;
    		}
    
    	}
    
    	/**
    	 * 移动时,进行边界判断,主要判断宽或高大于屏幕的
    	 */
    	private void checkMatrixBounds()
    	{
    		RectF rect = getMatrixRectF();
    
    		float deltaX = 0, deltaY = 0;
    		final float viewWidth = getWidth();
    		final float viewHeight = getHeight();
    		// 判断移动或缩放后,图片显示是否超出屏幕边界
    		if (rect.top > 0 && isCheckTopAndBottom)
    		{
    			deltaY = -rect.top;
    		}
    		if (rect.bottom < viewHeight && isCheckTopAndBottom)
    		{
    			deltaY = viewHeight - rect.bottom;
    		}
    		if (rect.left > 0 && isCheckLeftAndRight)
    		{
    			deltaX = -rect.left;
    		}
    		if (rect.right < viewWidth && isCheckLeftAndRight)
    		{
    			deltaX = viewWidth - rect.right;
    		}
    		mScaleMatrix.postTranslate(deltaX, deltaY);
    	}
    
    	/**
    	 * 是否是推动行为
    	 * 
    	 * @param dx
    	 * @param dy
    	 * @return
    	 */
    	private boolean isCanDrag(float dx, float dy)
    	{
    		return Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;
    	}
    
    }

     

    展开全文
  • 安卓手势放大图片,多点触控 两手指外张图片放大
  • * * 根据两指进行缩放; * * 缩放后回弹; * * 根据手指上下左右移动; */ @SuppressLint("AppCompatCustomView") public class PhotoView extends ImageView { private final static int MIN_ROTATE = 35; ...

    原贴地址 https://blog.csdn.net/wuqingsen1/article/details/84029503

    一开始是想点击图片弹出一个dialog后来发现不行,改为跳转到一个activity、即可。

    三个工具类:

    1、注意包的引用

    
    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Matrix;
    import android.graphics.PointF;
    import android.graphics.RectF;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.view.GestureDetector;
    import android.view.MotionEvent;
    import android.view.ScaleGestureDetector;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.ViewParent;
    import android.view.animation.DecelerateInterpolator;
    import android.view.animation.Interpolator;
    import android.widget.ImageView;
    import android.widget.OverScroller;
    import android.widget.Scroller;
    
    /**
     * describe:图片查看器工具类,包括:
     * * 双击放大、缩小;
     * * 根据两指进行缩放;
     * * 缩放后回弹;
     * * 根据手指上下左右移动;
    */
    
    @SuppressLint("AppCompatCustomView")
    public class PhotoView extends ImageView {
    
        private final static int MIN_ROTATE = 35;
        private final static int ANIMA_DURING = 340;
        private final static float MAX_SCALE = 2.5f;
    
        private int mMinRotate;
        private int mAnimaDuring;
        private float mMaxScale;
    
        private int MAX_OVER_SCROLL = 0;
        private int MAX_FLING_OVER_SCROLL = 0;
        private int MAX_OVER_RESISTANCE = 0;
        private int MAX_ANIM_FROM_WAITE = 500;
    
        private Matrix mBaseMatrix = new Matrix();
        private Matrix mAnimaMatrix = new Matrix();
        private Matrix mSynthesisMatrix = new Matrix();
        private Matrix mTmpMatrix = new Matrix();
    
        private RotateGestureDetector mRotateDetector;
        private GestureDetector mDetector;
        private ScaleGestureDetector mScaleDetector;
        private OnClickListener mClickListener;
    
        private ScaleType mScaleType;
    
        private boolean hasMultiTouch;
        private boolean hasDrawable;
        private boolean isKnowSize;
        private boolean hasOverTranslate;
        private boolean isEnable = false;
        private boolean isRotateEnable = false;
        private boolean isInit;
        private boolean mAdjustViewBounds;
        // 当前是否处于放大状态
        private boolean isZoonUp;
        private boolean canRotate;
    
        private boolean imgLargeWidth;
        private boolean imgLargeHeight;
    
        private float mRotateFlag;
        private float mDegrees;
        private float mScale = 1.0f;
        private int mTranslateX;
        private int mTranslateY;
    
        private float mHalfBaseRectWidth;
        private float mHalfBaseRectHeight;
    
        private RectF mWidgetRect = new RectF();
        private RectF mBaseRect = new RectF();
        private RectF mImgRect = new RectF();
        private RectF mTmpRect = new RectF();
        private RectF mCommonRect = new RectF();
    
        private PointF mScreenCenter = new PointF();
        private PointF mScaleCenter = new PointF();
        private PointF mRotateCenter = new PointF();
    
        private Transform mTranslate = new Transform();
    
        private RectF mClip;
        private Info mFromInfo;
        private long mInfoTime;
        private Runnable mCompleteCallBack;
    
        private OnLongClickListener mLongClick;
    
        public PhotoView(Context context) {
            super(context);
            init();
        }
    
        public PhotoView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public PhotoView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            super.setScaleType(ScaleType.MATRIX);
            if (mScaleType == null) mScaleType = ScaleType.CENTER_INSIDE;
            mRotateDetector = new RotateGestureDetector(mRotateListener);
            mDetector = new GestureDetector(getContext(), mGestureListener);
            mScaleDetector = new ScaleGestureDetector(getContext(), mScaleListener);
            float density = getResources().getDisplayMetrics().density;
            MAX_OVER_SCROLL = (int) (density * 30);
            MAX_FLING_OVER_SCROLL = (int) (density * 30);
            MAX_OVER_RESISTANCE = (int) (density * 140);
    
            mMinRotate = MIN_ROTATE;
            mAnimaDuring = ANIMA_DURING;
            mMaxScale = MAX_SCALE;
        }
    
        /**
         * 获取默认的动画持续时间
         */
        public int getDefaultAnimaDuring() {
            return ANIMA_DURING;
        }
    
        @Override
        public void setOnClickListener(OnClickListener l) {
            super.setOnClickListener(l);
            mClickListener = l;
        }
    
        @Override
        public void setScaleType(ScaleType scaleType) {
            if (scaleType == ScaleType.MATRIX) return;
    
            if (scaleType != mScaleType) {
                mScaleType = scaleType;
    
                if (isInit) {
                    initBase();
                }
            }
        }
    
        @Override
        public void setOnLongClickListener(OnLongClickListener l) {
            mLongClick = l;
        }
    
        /**
         * 设置动画的插入器
         */
        public void setInterpolator(Interpolator interpolator) {
            mTranslate.setInterpolator(interpolator);
        }
    
        /**
         * 获取动画持续时间
         */
        public int getAnimaDuring() {
            return mAnimaDuring;
        }
    
        /**
         * 设置动画的持续时间
         */
        public void setAnimaDuring(int during) {
            mAnimaDuring = during;
        }
    
        /**
         * 设置最大可以缩放的倍数
         */
        public void setMaxScale(float maxScale) {
            mMaxScale = maxScale;
        }
    
        /**
         * 获取最大可以缩放的倍数
         */
        public float getMaxScale() {
            return mMaxScale;
        }
    
        /**
         * 启用缩放功能
         */
        public void enable() {
            isEnable = true;
        }
    
        /**
         * 禁用缩放功能
         */
        public void disenable() {
            isEnable = false;
        }
    
        /**
         * 启用旋转功能
         */
        public void enableRotate() {
            isRotateEnable = true;
        }
    
        /**
         * 禁用旋转功能
         */
        public void disableRotate() {
            isRotateEnable = false;
        }
    
        /**
         */
        public void setMaxAnimFromWaiteTime(int wait) {
            MAX_ANIM_FROM_WAITE = wait;
        }
    
        @Override
        public void setImageResource(int resId) {
            Drawable drawable = null;
            try {
                drawable = getResources().getDrawable(resId);
            } catch (Exception e) {
            }
    
            setImageDrawable(drawable);
        }
    
        @Override
        public void setImageDrawable(Drawable drawable) {
            super.setImageDrawable(drawable);
    
            if (drawable == null) {
                hasDrawable = false;
                return;
            }
    
            if (!hasSize(drawable))
                return;
    
            if (!hasDrawable) {
                hasDrawable = true;
            }
    
            initBase();
        }
    
        private boolean hasSize(Drawable d) {
            if ((d.getIntrinsicHeight() <= 0 || d.getIntrinsicWidth() <= 0)
                    && (d.getMinimumWidth() <= 0 || d.getMinimumHeight() <= 0)
                    && (d.getBounds().width() <= 0 || d.getBounds().height() <= 0)) {
                return false;
            }
            return true;
        }
    
        private static int getDrawableWidth(Drawable d) {
            int width = d.getIntrinsicWidth();
            if (width <= 0) width = d.getMinimumWidth();
            if (width <= 0) width = d.getBounds().width();
            return width;
        }
    
        private static int getDrawableHeight(Drawable d) {
            int height = d.getIntrinsicHeight();
            if (height <= 0) height = d.getMinimumHeight();
            if (height <= 0) height = d.getBounds().height();
            return height;
        }
    
        private void initBase() {
            if (!hasDrawable) return;
            if (!isKnowSize) return;
    
            mBaseMatrix.reset();
            mAnimaMatrix.reset();
    
            isZoonUp = false;
    
            Drawable img = getDrawable();
    
            int w = getWidth();
            int h = getHeight();
            int imgw = getDrawableWidth(img);
            int imgh = getDrawableHeight(img);
    
            mBaseRect.set(0, 0, imgw, imgh);
    
            // 以图片中心点居中位移
            int tx = (w - imgw) / 2;
            int ty = (h - imgh) / 2;
    
            float sx = 1;
            float sy = 1;
    
            // 缩放,默认不超过屏幕大小
            if (imgw > w) {
                sx = (float) w / imgw;
            }
    
            if (imgh > h) {
                sy = (float) h / imgh;
            }
    
            float scale = sx < sy ? sx : sy;
    
            mBaseMatrix.reset();
            mBaseMatrix.postTranslate(tx, ty);
            mBaseMatrix.postScale(scale, scale, mScreenCenter.x, mScreenCenter.y);
            mBaseMatrix.mapRect(mBaseRect);
    
            mHalfBaseRectWidth = mBaseRect.width() / 2;
            mHalfBaseRectHeight = mBaseRect.height() / 2;
    
            mScaleCenter.set(mScreenCenter);
            mRotateCenter.set(mScaleCenter);
    
            executeTranslate();
    
            switch (mScaleType) {
                case CENTER:
                    initCenter();
                    break;
                case CENTER_CROP:
                    initCenterCrop();
                    break;
                case CENTER_INSIDE:
                    initCenterInside();
                    break;
                case FIT_CENTER:
                    initFitCenter();
                    break;
                case FIT_START:
                    initFitStart();
                    break;
                case FIT_END:
                    initFitEnd();
                    break;
                case FIT_XY:
                    initFitXY();
                    break;
            }
    
            isInit = true;
    
            if (mFromInfo != null && System.currentTimeMillis() - mInfoTime < MAX_ANIM_FROM_WAITE) {
                animaFrom(mFromInfo);
            }
    
            mFromInfo = null;
        }
    
        private void initCenter() {
            if (!hasDrawable) return;
            if (!isKnowSize) return;
    
            Drawable img = getDrawable();
    
            int imgw = getDrawableWidth(img);
            int imgh = getDrawableHeight(img);
    
            if (imgw > mWidgetRect.width() || imgh > mWidgetRect.height()) {
                float scaleX = imgw / mImgRect.width();
                float scaleY = imgh / mImgRect.height();
    
                mScale = scaleX > scaleY ? scaleX : scaleY;
    
                mAnimaMatrix.postScale(mScale, mScale, mScreenCenter.x, mScreenCenter.y);
    
                executeTranslate();
    
                resetBase();
            }
        }
    
        private void initCenterCrop() {
            if (mImgRect.width() < mWidgetRect.width() || mImgRect.height() < mWidgetRect.height()) {
                float scaleX = mWidgetRect.width() / mImgRect.width();
                float scaleY = mWidgetRect.height() / mImgRect.height();
    
                mScale = scaleX > scaleY ? scaleX : scaleY;
    
                mAnimaMatrix.postScale(mScale, mScale, mScreenCenter.x, mScreenCenter.y);
    
                executeTranslate();
                resetBase();
            }
        }
    
        private void initCenterInside() {
            if (mImgRect.width() > mWidgetRect.width() || mImgRect.height() > mWidgetRect.height()) {
                float scaleX = mWidgetRect.width() / mImgRect.width();
                float scaleY = mWidgetRect.height() / mImgRect.height();
    
                mScale = scaleX < scaleY ? scaleX : scaleY;
    
                mAnimaMatrix.postScale(mScale, mScale, mScreenCenter.x, mScreenCenter.y);
    
                executeTranslate();
                resetBase();
            }
        }
    
        private void initFitCenter() {
            if (mImgRect.width() < mWidgetRect.width()) {
                mScale = mWidgetRect.width() / mImgRect.width();
    
                mAnimaMatrix.postScale(mScale, mScale, mScreenCenter.x, mScreenCenter.y);
    
                executeTranslate();
                resetBase();
            }
        }
    
        private void initFitStart() {
            initFitCenter();
    
            float ty = -mImgRect.top;
            mAnimaMatrix.postTranslate(0, ty);
            executeTranslate();
            resetBase();
            mTranslateY += ty;
        }
    
        private void initFitEnd() {
            initFitCenter();
    
            float ty = (mWidgetRect.bottom - mImgRect.bottom);
            mTranslateY += ty;
            mAnimaMatrix.postTranslate(0, ty);
            executeTranslate();
            resetBase();
        }
    
        private void initFitXY() {
            float scaleX = mWidgetRect.width() / mImgRect.width();
            float scaleY = mWidgetRect.height() / mImgRect.height();
    
            mAnimaMatrix.postScale(scaleX, scaleY, mScreenCenter.x, mScreenCenter.y);
    
            executeTranslate();
            resetBase();
        }
    
        private void resetBase() {
            Drawable img = getDrawable();
            int imgw = getDrawableWidth(img);
            int imgh = getDrawableHeight(img);
            mBaseRect.set(0, 0, imgw, imgh);
            mBaseMatrix.set(mSynthesisMatrix);
            mBaseMatrix.mapRect(mBaseRect);
            mHalfBaseRectWidth = mBaseRect.width() / 2;
            mHalfBaseRectHeight = mBaseRect.height() / 2;
            mScale = 1;
            mTranslateX = 0;
            mTranslateY = 0;
            mAnimaMatrix.reset();
        }
    
        private void executeTranslate() {
            mSynthesisMatrix.set(mBaseMatrix);
            mSynthesisMatrix.postConcat(mAnimaMatrix);
            setImageMatrix(mSynthesisMatrix);
    
            mAnimaMatrix.mapRect(mImgRect, mBaseRect);
    
            imgLargeWidth = mImgRect.width() > mWidgetRect.width();
            imgLargeHeight = mImgRect.height() > mWidgetRect.height();
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            if (!hasDrawable) {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
                return;
            }
    
            Drawable d = getDrawable();
            int drawableW = getDrawableWidth(d);
            int drawableH = getDrawableHeight(d);
    
            int pWidth = MeasureSpec.getSize(widthMeasureSpec);
            int pHeight = MeasureSpec.getSize(heightMeasureSpec);
    
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    
            int width = 0;
            int height = 0;
    
            ViewGroup.LayoutParams p = getLayoutParams();
    
            if (p == null) {
                p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            }
    
            if (p.width == ViewGroup.LayoutParams.MATCH_PARENT) {
                if (widthMode == MeasureSpec.UNSPECIFIED) {
                    width = drawableW;
                } else {
                    width = pWidth;
                }
            } else {
                if (widthMode == MeasureSpec.EXACTLY) {
                    width = pWidth;
                } else if (widthMode == MeasureSpec.AT_MOST) {
                    width = drawableW > pWidth ? pWidth : drawableW;
                } else {
                    width = drawableW;
                }
            }
    
            if (p.height == ViewGroup.LayoutParams.MATCH_PARENT) {
                if (heightMode == MeasureSpec.UNSPECIFIED) {
                    height = drawableH;
                } else {
                    height = pHeight;
                }
            } else {
                if (heightMode == MeasureSpec.EXACTLY) {
                    height = pHeight;
                } else if (heightMode == MeasureSpec.AT_MOST) {
                    height = drawableH > pHeight ? pHeight : drawableH;
                } else {
                    height = drawableH;
                }
            }
    
            if (mAdjustViewBounds && (float) drawableW / drawableH != (float) width / height) {
    
                float hScale = (float) height / drawableH;
                float wScale = (float) width / drawableW;
    
                float scale = hScale < wScale ? hScale : wScale;
                width = p.width == ViewGroup.LayoutParams.MATCH_PARENT ? width : (int) (drawableW * scale);
                height = p.height == ViewGroup.LayoutParams.MATCH_PARENT ? height : (int) (drawableH * scale);
            }
    
            setMeasuredDimension(width, height);
        }
    
        @Override
        public void setAdjustViewBounds(boolean adjustViewBounds) {
            super.setAdjustViewBounds(adjustViewBounds);
            mAdjustViewBounds = adjustViewBounds;
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
    
            mWidgetRect.set(0, 0, w, h);
            mScreenCenter.set(w / 2, h / 2);
    
            if (!isKnowSize) {
                isKnowSize = true;
                initBase();
            }
        }
    
        @Override
        public void draw(Canvas canvas) {
            if (mClip != null) {
                canvas.clipRect(mClip);
                mClip = null;
            }
            super.draw(canvas);
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if (isEnable) {
                final int Action = event.getActionMasked();
                if (event.getPointerCount() >= 2) hasMultiTouch = true;
    
                mDetector.onTouchEvent(event);
                if (isRotateEnable) {
                    mRotateDetector.onTouchEvent(event);
                }
                mScaleDetector.onTouchEvent(event);
    
                if (Action == MotionEvent.ACTION_UP || Action == MotionEvent.ACTION_CANCEL) onUp();
    
                return true;
            } else {
                return super.dispatchTouchEvent(event);
            }
        }
    
        private void onUp() {
            if (mTranslate.isRuning) return;
    
            if (canRotate || mDegrees % 90 != 0) {
                float toDegrees = (int) (mDegrees / 90) * 90;
                float remainder = mDegrees % 90;
    
                if (remainder > 45)
                    toDegrees += 90;
                else if (remainder < -45)
                    toDegrees -= 90;
    
                mTranslate.withRotate((int) mDegrees, (int) toDegrees);
    
                mDegrees = toDegrees;
            }
    
            float scale = mScale;
    
            if (mScale < 1) {
                scale = 1;
                mTranslate.withScale(mScale, 1);
            } else if (mScale > mMaxScale) {
                scale = mMaxScale;
                mTranslate.withScale(mScale, mMaxScale);
            }
    
            float cx = mImgRect.left + mImgRect.width() / 2;
            float cy = mImgRect.top + mImgRect.height() / 2;
    
            mScaleCenter.set(cx, cy);
            mRotateCenter.set(cx, cy);
    
            mTranslateX = 0;
            mTranslateY = 0;
    
            mTmpMatrix.reset();
            mTmpMatrix.postTranslate(-mBaseRect.left, -mBaseRect.top);
            mTmpMatrix.postTranslate(cx - mHalfBaseRectWidth, cy - mHalfBaseRectHeight);
            mTmpMatrix.postScale(scale, scale, cx, cy);
            mTmpMatrix.postRotate(mDegrees, cx, cy);
            mTmpMatrix.mapRect(mTmpRect, mBaseRect);
    
            doTranslateReset(mTmpRect);
            mTranslate.start();
        }
    
        private void doTranslateReset(RectF imgRect) {
            int tx = 0;
            int ty = 0;
    
            if (imgRect.width() <= mWidgetRect.width()) {
                if (!isImageCenterWidth(imgRect))
                    tx = -(int) ((mWidgetRect.width() - imgRect.width()) / 2 - imgRect.left);
            } else {
                if (imgRect.left > mWidgetRect.left) {
                    tx = (int) (imgRect.left - mWidgetRect.left);
                } else if (imgRect.right < mWidgetRect.right) {
                    tx = (int) (imgRect.right - mWidgetRect.right);
                }
            }
    
            if (imgRect.height() <= mWidgetRect.height()) {
                if (!isImageCenterHeight(imgRect))
                    ty = -(int) ((mWidgetRect.height() - imgRect.height()) / 2 - imgRect.top);
            } else {
                if (imgRect.top > mWidgetRect.top) {
                    ty = (int) (imgRect.top - mWidgetRect.top);
                } else if (imgRect.bottom < mWidgetRect.bottom) {
                    ty = (int) (imgRect.bottom - mWidgetRect.bottom);
                }
            }
    
            if (tx != 0 || ty != 0) {
                if (!mTranslate.mFlingScroller.isFinished()) mTranslate.mFlingScroller.abortAnimation();
                mTranslate.withTranslate(mTranslateX, mTranslateY, -tx, -ty);
            }
        }
    
        private boolean isImageCenterHeight(RectF rect) {
            return Math.abs(Math.round(rect.top) - (mWidgetRect.height() - rect.height()) / 2) < 1;
        }
    
        private boolean isImageCenterWidth(RectF rect) {
            return Math.abs(Math.round(rect.left) - (mWidgetRect.width() - rect.width()) / 2) < 1;
        }
    
        private OnRotateListener mRotateListener = new OnRotateListener() {
    
            @Override
            public void onRotate(float degrees, float focusX, float focusY) {
                mRotateFlag += degrees;
                if (canRotate) {
                    mDegrees += degrees;
                    mAnimaMatrix.postRotate(degrees, focusX, focusY);
                } else {
                    if (Math.abs(mRotateFlag) >= mMinRotate) {
                        canRotate = true;
                        mRotateFlag = 0;
                    }
                }
            }
        };
    
        private ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() {
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                float scaleFactor = detector.getScaleFactor();
    
                if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))
                    return false;
    
                mScale *= scaleFactor;
    //            mScaleCenter.set(detector.getFocusX(), detector.getFocusY());
                mAnimaMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
                executeTranslate();
                return true;
            }
    
            public boolean onScaleBegin(ScaleGestureDetector detector) {
                return true;
            }
    
            public void onScaleEnd(ScaleGestureDetector detector) {
    
            }
        };
    
        private float resistanceScrollByX(float overScroll, float detalX) {
            float s = detalX * (Math.abs(Math.abs(overScroll) - MAX_OVER_RESISTANCE) / (float) MAX_OVER_RESISTANCE);
            return s;
        }
    
        private float resistanceScrollByY(float overScroll, float detalY) {
            float s = detalY * (Math.abs(Math.abs(overScroll) - MAX_OVER_RESISTANCE) / (float) MAX_OVER_RESISTANCE);
            return s;
        }
    
        /**
         * 匹配两个Rect的共同部分输出到out,若无共同部分则输出0,0,0,0
         */
        private void mapRect(RectF r1, RectF r2, RectF out) {
    
            float l, r, t, b;
    
            l = r1.left > r2.left ? r1.left : r2.left;
            r = r1.right < r2.right ? r1.right : r2.right;
    
            if (l > r) {
                out.set(0, 0, 0, 0);
                return;
            }
    
            t = r1.top > r2.top ? r1.top : r2.top;
            b = r1.bottom < r2.bottom ? r1.bottom : r2.bottom;
    
            if (t > b) {
                out.set(0, 0, 0, 0);
                return;
            }
    
            out.set(l, t, r, b);
        }
    
        private void checkRect() {
            if (!hasOverTranslate) {
                mapRect(mWidgetRect, mImgRect, mCommonRect);
            }
        }
    
        private Runnable mClickRunnable = new Runnable() {
            @Override
            public void run() {
                if (mClickListener != null) {
                    mClickListener.onClick(PhotoView.this);
                }
            }
        };
    
        private GestureDetector.OnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() {
    
            @Override
            public void onLongPress(MotionEvent e) {
                if (mLongClick != null) {
                    mLongClick.onLongClick(PhotoView.this);
                }
            }
    
            @Override
            public boolean onDown(MotionEvent e) {
                hasOverTranslate = false;
                hasMultiTouch = false;
                canRotate = false;
                removeCallbacks(mClickRunnable);
                return false;
            }
    
            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                if (hasMultiTouch) return false;
                if (!imgLargeWidth && !imgLargeHeight) return false;
                if (mTranslate.isRuning) return false;
    
                float vx = velocityX;
                float vy = velocityY;
    
                if (Math.round(mImgRect.left) >= mWidgetRect.left || Math.round(mImgRect.right) <= mWidgetRect.right) {
                    vx = 0;
                }
    
                if (Math.round(mImgRect.top) >= mWidgetRect.top || Math.round(mImgRect.bottom) <= mWidgetRect.bottom) {
                    vy = 0;
                }
    
                if (canRotate || mDegrees % 90 != 0) {
                    float toDegrees = (int) (mDegrees / 90) * 90;
                    float remainder = mDegrees % 90;
    
                    if (remainder > 45)
                        toDegrees += 90;
                    else if (remainder < -45)
                        toDegrees -= 90;
    
                    mTranslate.withRotate((int) mDegrees, (int) toDegrees);
    
                    mDegrees = toDegrees;
                }
    
                doTranslateReset(mImgRect);
    
                mTranslate.withFling(vx, vy);
    
                mTranslate.start();
                // onUp(e2);
                return super.onFling(e1, e2, velocityX, velocityY);
            }
    
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                if (mTranslate.isRuning) {
                    mTranslate.stop();
                }
    
                if (canScrollHorizontallySelf(distanceX)) {
                    if (distanceX < 0 && mImgRect.left - distanceX > mWidgetRect.left)
                        distanceX = mImgRect.left;
                    if (distanceX > 0 && mImgRect.right - distanceX < mWidgetRect.right)
                        distanceX = mImgRect.right - mWidgetRect.right;
    
                    mAnimaMatrix.postTranslate(-distanceX, 0);
                    mTranslateX -= distanceX;
                } else if (imgLargeWidth || hasMultiTouch || hasOverTranslate) {
                    checkRect();
                    if (!hasMultiTouch) {
                        if (distanceX < 0 && mImgRect.left - distanceX > mCommonRect.left)
                            distanceX = resistanceScrollByX(mImgRect.left - mCommonRect.left, distanceX);
                        if (distanceX > 0 && mImgRect.right - distanceX < mCommonRect.right)
                            distanceX = resistanceScrollByX(mImgRect.right - mCommonRect.right, distanceX);
                    }
    
                    mTranslateX -= distanceX;
                    mAnimaMatrix.postTranslate(-distanceX, 0);
                    hasOverTranslate = true;
                }
    
                if (canScrollVerticallySelf(distanceY)) {
                    if (distanceY < 0 && mImgRect.top - distanceY > mWidgetRect.top)
                        distanceY = mImgRect.top;
                    if (distanceY > 0 && mImgRect.bottom - distanceY < mWidgetRect.bottom)
                        distanceY = mImgRect.bottom - mWidgetRect.bottom;
    
                    mAnimaMatrix.postTranslate(0, -distanceY);
                    mTranslateY -= distanceY;
                } else if (imgLargeHeight || hasOverTranslate || hasMultiTouch) {
                    checkRect();
                    if (!hasMultiTouch) {
                        if (distanceY < 0 && mImgRect.top - distanceY > mCommonRect.top)
                            distanceY = resistanceScrollByY(mImgRect.top - mCommonRect.top, distanceY);
                        if (distanceY > 0 && mImgRect.bottom - distanceY < mCommonRect.bottom)
                            distanceY = resistanceScrollByY(mImgRect.bottom - mCommonRect.bottom, distanceY);
                    }
    
                    mAnimaMatrix.postTranslate(0, -distanceY);
                    mTranslateY -= distanceY;
                    hasOverTranslate = true;
                }
    
                executeTranslate();
                return true;
            }
    
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                postDelayed(mClickRunnable, 250);
                return false;
            }
    
            @Override
            public boolean onDoubleTap(MotionEvent e) {
    
                mTranslate.stop();
    
                float from = 1;
                float to = 1;
    
                float imgcx = mImgRect.left + mImgRect.width() / 2;
                float imgcy = mImgRect.top + mImgRect.height() / 2;
    
                mScaleCenter.set(imgcx, imgcy);
                mRotateCenter.set(imgcx, imgcy);
                mTranslateX = 0;
                mTranslateY = 0;
    
                if (isZoonUp) {
                    from = mScale;
                    to = 1;
                } else {
                    from = mScale;
                    to = mMaxScale;
    
                    mScaleCenter.set(e.getX(), e.getY());
                }
    
                mTmpMatrix.reset();
                mTmpMatrix.postTranslate(-mBaseRect.left, -mBaseRect.top);
                mTmpMatrix.postTranslate(mRotateCenter.x, mRotateCenter.y);
                mTmpMatrix.postTranslate(-mHalfBaseRectWidth, -mHalfBaseRectHeight);
                mTmpMatrix.postRotate(mDegrees, mRotateCenter.x, mRotateCenter.y);
                mTmpMatrix.postScale(to, to, mScaleCenter.x, mScaleCenter.y);
                mTmpMatrix.postTranslate(mTranslateX, mTranslateY);
                mTmpMatrix.mapRect(mTmpRect, mBaseRect);
                doTranslateReset(mTmpRect);
    
                isZoonUp = !isZoonUp;
                mTranslate.withScale(from, to);
                mTranslate.start();
    
                return false;
            }
        };
    
        public boolean canScrollHorizontallySelf(float direction) {
            if (mImgRect.width() <= mWidgetRect.width()) return false;
            if (direction < 0 && Math.round(mImgRect.left) - direction >= mWidgetRect.left)
                return false;
            if (direction > 0 && Math.round(mImgRect.right) - direction <= mWidgetRect.right)
                return false;
            return true;
        }
    
        public boolean canScrollVerticallySelf(float direction) {
            if (mImgRect.height() <= mWidgetRect.height()) return false;
            if (direction < 0 && Math.round(mImgRect.top) - direction >= mWidgetRect.top)
                return false;
            if (direction > 0 && Math.round(mImgRect.bottom) - direction <= mWidgetRect.bottom)
                return false;
            return true;
        }
    
        @Override
        public boolean canScrollHorizontally(int direction) {
            if (hasMultiTouch) return true;
            return canScrollHorizontallySelf(direction);
        }
    
        @Override
        public boolean canScrollVertically(int direction) {
            if (hasMultiTouch) return true;
            return canScrollVerticallySelf(direction);
        }
    
        private class InterpolatorProxy implements Interpolator {
    
            private Interpolator mTarget;
    
            private InterpolatorProxy() {
                mTarget = new DecelerateInterpolator();
            }
    
            public void setTargetInterpolator(Interpolator interpolator) {
                mTarget = interpolator;
            }
    
            @Override
            public float getInterpolation(float input) {
                if (mTarget != null) {
                    return mTarget.getInterpolation(input);
                }
                return input;
            }
        }
    
        private class Transform implements Runnable {
    
            boolean isRuning;
    
            OverScroller mTranslateScroller;
            OverScroller mFlingScroller;
            Scroller mScaleScroller;
            Scroller mClipScroller;
            Scroller mRotateScroller;
    
            ClipCalculate C;
    
            int mLastFlingX;
            int mLastFlingY;
    
            int mLastTranslateX;
            int mLastTranslateY;
    
            RectF mClipRect = new RectF();
    
            InterpolatorProxy mInterpolatorProxy = new InterpolatorProxy();
    
            Transform() {
                Context ctx = getContext();
                mTranslateScroller = new OverScroller(ctx, mInterpolatorProxy);
                mScaleScroller = new Scroller(ctx, mInterpolatorProxy);
                mFlingScroller = new OverScroller(ctx, mInterpolatorProxy);
                mClipScroller = new Scroller(ctx, mInterpolatorProxy);
                mRotateScroller = new Scroller(ctx, mInterpolatorProxy);
            }
    
            public void setInterpolator(Interpolator interpolator) {
                mInterpolatorProxy.setTargetInterpolator(interpolator);
            }
    
            void withTranslate(int startX, int startY, int deltaX, int deltaY) {
                mLastTranslateX = 0;
                mLastTranslateY = 0;
                mTranslateScroller.startScroll(0, 0, deltaX, deltaY, mAnimaDuring);
            }
    
            void withScale(float form, float to) {
                mScaleScroller.startScroll((int) (form * 10000), 0, (int) ((to - form) * 10000), 0, mAnimaDuring);
            }
    
            void withClip(float fromX, float fromY, float deltaX, float deltaY, int d, ClipCalculate c) {
                mClipScroller.startScroll((int) (fromX * 10000), (int) (fromY * 10000), (int) (deltaX * 10000), (int) (deltaY * 10000), d);
                C = c;
            }
    
            void withRotate(int fromDegrees, int toDegrees) {
                mRotateScroller.startScroll(fromDegrees, 0, toDegrees - fromDegrees, 0, mAnimaDuring);
            }
    
            void withRotate(int fromDegrees, int toDegrees, int during) {
                mRotateScroller.startScroll(fromDegrees, 0, toDegrees - fromDegrees, 0, during);
            }
    
            void withFling(float velocityX, float velocityY) {
                mLastFlingX = velocityX < 0 ? Integer.MAX_VALUE : 0;
                int distanceX = (int) (velocityX > 0 ? Math.abs(mImgRect.left) : mImgRect.right - mWidgetRect.right);
                distanceX = velocityX < 0 ? Integer.MAX_VALUE - distanceX : distanceX;
                int minX = velocityX < 0 ? distanceX : 0;
                int maxX = velocityX < 0 ? Integer.MAX_VALUE : distanceX;
                int overX = velocityX < 0 ? Integer.MAX_VALUE - minX : distanceX;
    
                mLastFlingY = velocityY < 0 ? Integer.MAX_VALUE : 0;
                int distanceY = (int) (velocityY > 0 ? Math.abs(mImgRect.top) : mImgRect.bottom - mWidgetRect.bottom);
                distanceY = velocityY < 0 ? Integer.MAX_VALUE - distanceY : distanceY;
                int minY = velocityY < 0 ? distanceY : 0;
                int maxY = velocityY < 0 ? Integer.MAX_VALUE : distanceY;
                int overY = velocityY < 0 ? Integer.MAX_VALUE - minY : distanceY;
    
                if (velocityX == 0) {
                    maxX = 0;
                    minX = 0;
                }
    
                if (velocityY == 0) {
                    maxY = 0;
                    minY = 0;
                }
    
                mFlingScroller.fling(mLastFlingX, mLastFlingY, (int) velocityX, (int) velocityY, minX, maxX, minY, maxY, Math.abs(overX) < MAX_FLING_OVER_SCROLL * 2 ? 0 : MAX_FLING_OVER_SCROLL, Math.abs(overY) < MAX_FLING_OVER_SCROLL * 2 ? 0 : MAX_FLING_OVER_SCROLL);
            }
    
            void start() {
                isRuning = true;
                postExecute();
            }
    
            void stop() {
                removeCallbacks(this);
                mTranslateScroller.abortAnimation();
                mScaleScroller.abortAnimation();
                mFlingScroller.abortAnimation();
                mRotateScroller.abortAnimation();
                isRuning = false;
            }
    
            @Override
            public void run() {
    
                // if (!isRuning) return;
    
                boolean endAnima = true;
    
                if (mScaleScroller.computeScrollOffset()) {
                    mScale = mScaleScroller.getCurrX() / 10000f;
                    endAnima = false;
                }
    
                if (mTranslateScroller.computeScrollOffset()) {
                    int tx = mTranslateScroller.getCurrX() - mLastTranslateX;
                    int ty = mTranslateScroller.getCurrY() - mLastTranslateY;
                    mTranslateX += tx;
                    mTranslateY += ty;
                    mLastTranslateX = mTranslateScroller.getCurrX();
                    mLastTranslateY = mTranslateScroller.getCurrY();
                    endAnima = false;
                }
    
                if (mFlingScroller.computeScrollOffset()) {
                    int x = mFlingScroller.getCurrX() - mLastFlingX;
                    int y = mFlingScroller.getCurrY() - mLastFlingY;
    
                    mLastFlingX = mFlingScroller.getCurrX();
                    mLastFlingY = mFlingScroller.getCurrY();
    
                    mTranslateX += x;
                    mTranslateY += y;
                    endAnima = false;
                }
    
                if (mRotateScroller.computeScrollOffset()) {
                    mDegrees = mRotateScroller.getCurrX();
                    endAnima = false;
                }
    
                if (mClipScroller.computeScrollOffset() || mClip != null) {
                    float sx = mClipScroller.getCurrX() / 10000f;
                    float sy = mClipScroller.getCurrY() / 10000f;
                    mTmpMatrix.setScale(sx, sy, (mImgRect.left + mImgRect.right) / 2, C.calculateTop());
                    mTmpMatrix.mapRect(mClipRect, mImgRect);
    
                    if (sx == 1) {
                        mClipRect.left = mWidgetRect.left;
                        mClipRect.right = mWidgetRect.right;
                    }
    
                    if (sy == 1) {
                        mClipRect.top = mWidgetRect.top;
                        mClipRect.bottom = mWidgetRect.bottom;
                    }
    
                    mClip = mClipRect;
                }
    
                if (!endAnima) {
                    applyAnima();
                    postExecute();
                } else {
                    isRuning = false;
    
                    // 修复动画结束后边距有些空隙,
                    boolean needFix = false;
    
                    if (imgLargeWidth) {
                        if (mImgRect.left > 0) {
                            mTranslateX -= mImgRect.left;
                        } else if (mImgRect.right < mWidgetRect.width()) {
                            mTranslateX -= (int) (mWidgetRect.width() - mImgRect.right);
                        }
                        needFix = true;
                    }
    
                    if (imgLargeHeight) {
                        if (mImgRect.top > 0) {
                            mTranslateY -= mImgRect.top;
                        } else if (mImgRect.bottom < mWidgetRect.height()) {
                            mTranslateY -= (int) (mWidgetRect.height() - mImgRect.bottom);
                        }
                        needFix = true;
                    }
    
                    if (needFix) {
                        applyAnima();
                    }
    
                    invalidate();
    
                    if (mCompleteCallBack != null) {
                        mCompleteCallBack.run();
                        mCompleteCallBack = null;
                    }
                }
            }
    
            private void applyAnima() {
                mAnimaMatrix.reset();
                mAnimaMatrix.postTranslate(-mBaseRect.left, -mBaseRect.top);
                mAnimaMatrix.postTranslate(mRotateCenter.x, mRotateCenter.y);
                mAnimaMatrix.postTranslate(-mHalfBaseRectWidth, -mHalfBaseRectHeight);
                mAnimaMatrix.postRotate(mDegrees, mRotateCenter.x, mRotateCenter.y);
                mAnimaMatrix.postScale(mScale, mScale, mScaleCenter.x, mScaleCenter.y);
                mAnimaMatrix.postTranslate(mTranslateX, mTranslateY);
                executeTranslate();
            }
    
    
            private void postExecute() {
                if (isRuning) post(this);
            }
        }
    
        public Info getInfo() {
            RectF rect = new RectF();
            int[] p = new int[2];
            getLocation(this, p);
            rect.set(p[0] + mImgRect.left, p[1] + mImgRect.top, p[0] + mImgRect.right, p[1] + mImgRect.bottom);
            return new Info(rect, mImgRect, mWidgetRect, mBaseRect, mScreenCenter, mScale, mDegrees, mScaleType);
        }
    
        public static Info getImageViewInfo(ImageView imgView) {
            int[] p = new int[2];
            getLocation(imgView, p);
    
            Drawable drawable = imgView.getDrawable();
    
            Matrix matrix = imgView.getImageMatrix();
    
            int width = getDrawableWidth(drawable);
            int height = getDrawableHeight(drawable);
    
            RectF imgRect = new RectF(0, 0, width, height);
            matrix.mapRect(imgRect);
    
            RectF rect = new RectF(p[0] + imgRect.left, p[1] + imgRect.top, p[0] + imgRect.right, p[1] + imgRect.bottom);
            RectF widgetRect = new RectF(0, 0, imgView.getWidth(), imgView.getHeight());
            RectF baseRect = new RectF(widgetRect);
            PointF screenCenter = new PointF(widgetRect.width() / 2, widgetRect.height() / 2);
    
            return new Info(rect, imgRect, widgetRect, baseRect, screenCenter, 1, 0, imgView.getScaleType());
        }
    
        private static void getLocation(View target, int[] position) {
    
            position[0] += target.getLeft();
            position[1] += target.getTop();
    
            ViewParent viewParent = target.getParent();
            while (viewParent instanceof View) {
                final View view = (View) viewParent;
    
                if (view.getId() == android.R.id.content) return;
    
                position[0] -= view.getScrollX();
                position[1] -= view.getScrollY();
    
                position[0] += view.getLeft();
                position[1] += view.getTop();
    
                viewParent = view.getParent();
            }
    
            position[0] = (int) (position[0] + 0.5f);
            position[1] = (int) (position[1] + 0.5f);
        }
    
        private void reset() {
            mAnimaMatrix.reset();
            executeTranslate();
            mScale = 1;
            mTranslateX = 0;
            mTranslateY = 0;
        }
    
        public interface ClipCalculate {
            float calculateTop();
        }
    
        public class START implements ClipCalculate {
            public float calculateTop() {
                return mImgRect.top;
            }
        }
    
        public class END implements ClipCalculate {
            public float calculateTop() {
                return mImgRect.bottom;
            }
        }
    
        public class OTHER implements ClipCalculate {
            public float calculateTop() {
                return (mImgRect.top + mImgRect.bottom) / 2;
            }
        }
    
        /**
         * 在PhotoView内部还没有图片的时候同样可以调用该方法
         * <p></p>
         * 此时并不会播放动画,当给PhotoView设置图片后会自动播放动画。
         * <p></p>
         * 若等待时间过长也没有给控件设置图片,则会忽略该动画,若要再次播放动画则需要重新调用该方法
         * (等待的时间默认500毫秒,可以通过setMaxAnimFromWaiteTime(int)设置最大等待时间)
         */
        public void animaFrom(Info info) {
            if (isInit) {
                reset();
    
                Info mine = getInfo();
    
                float scaleX = info.mImgRect.width() / mine.mImgRect.width();
                float scaleY = info.mImgRect.height() / mine.mImgRect.height();
                float scale = scaleX < scaleY ? scaleX : scaleY;
    
                float ocx = info.mRect.left + info.mRect.width() / 2;
                float ocy = info.mRect.top + info.mRect.height() / 2;
    
                float mcx = mine.mRect.left + mine.mRect.width() / 2;
                float mcy = mine.mRect.top + mine.mRect.height() / 2;
    
                mAnimaMatrix.reset();
                // mAnimaMatrix.postTranslate(-mBaseRect.left, -mBaseRect.top);
                mAnimaMatrix.postTranslate(ocx - mcx, ocy - mcy);
                mAnimaMatrix.postScale(scale, scale, ocx, ocy);
                mAnimaMatrix.postRotate(info.mDegrees, ocx, ocy);
                executeTranslate();
    
                mScaleCenter.set(ocx, ocy);
                mRotateCenter.set(ocx, ocy);
    
                mTranslate.withTranslate(0, 0, (int) -(ocx - mcx), (int) -(ocy - mcy));
                mTranslate.withScale(scale, 1);
                mTranslate.withRotate((int) info.mDegrees, 0);
    
                if (info.mWidgetRect.width() < info.mImgRect.width() || info.mWidgetRect.height() < info.mImgRect.height()) {
                    float clipX = info.mWidgetRect.width() / info.mImgRect.width();
                    float clipY = info.mWidgetRect.height() / info.mImgRect.height();
                    clipX = clipX > 1 ? 1 : clipX;
                    clipY = clipY > 1 ? 1 : clipY;
    
                    ClipCalculate c = info.mScaleType == ScaleType.FIT_START ? new START() : info.mScaleType == ScaleType.FIT_END ? new END() : new OTHER();
    
                    mTranslate.withClip(clipX, clipY, 1 - clipX, 1 - clipY, mAnimaDuring / 3, c);
    
                    mTmpMatrix.setScale(clipX, clipY, (mImgRect.left + mImgRect.right) / 2, c.calculateTop());
                    mTmpMatrix.mapRect(mTranslate.mClipRect, mImgRect);
                    mClip = mTranslate.mClipRect;
                }
    
                mTranslate.start();
            } else {
                mFromInfo = info;
                mInfoTime = System.currentTimeMillis();
            }
        }
    
        public void animaTo(Info info, Runnable completeCallBack) {
            if (isInit) {
                mTranslate.stop();
    
                mTranslateX = 0;
                mTranslateY = 0;
    
                float tcx = info.mRect.left + info.mRect.width() / 2;
                float tcy = info.mRect.top + info.mRect.height() / 2;
    
                mScaleCenter.set(mImgRect.left + mImgRect.width() / 2, mImgRect.top + mImgRect.height() / 2);
                mRotateCenter.set(mScaleCenter);
    
                // 将图片旋转回正常位置,用以计算
                mAnimaMatrix.postRotate(-mDegrees, mScaleCenter.x, mScaleCenter.y);
                mAnimaMatrix.mapRect(mImgRect, mBaseRect);
    
                // 缩放
                float scaleX = info.mImgRect.width() / mBaseRect.width();
                float scaleY = info.mImgRect.height() / mBaseRect.height();
                float scale = scaleX > scaleY ? scaleX : scaleY;
    
                mAnimaMatrix.postRotate(mDegrees, mScaleCenter.x, mScaleCenter.y);
                mAnimaMatrix.mapRect(mImgRect, mBaseRect);
    
                mDegrees = mDegrees % 360;
    
                mTranslate.withTranslate(0, 0, (int) (tcx - mScaleCenter.x), (int) (tcy - mScaleCenter.y));
                mTranslate.withScale(mScale, scale);
                mTranslate.withRotate((int) mDegrees, (int) info.mDegrees, mAnimaDuring * 2 / 3);
    
                if (info.mWidgetRect.width() < info.mRect.width() || info.mWidgetRect.height() < info.mRect.height()) {
                    float clipX = info.mWidgetRect.width() / info.mRect.width();
                    float clipY = info.mWidgetRect.height() / info.mRect.height();
                    clipX = clipX > 1 ? 1 : clipX;
                    clipY = clipY > 1 ? 1 : clipY;
    
                    final float cx = clipX;
                    final float cy = clipY;
                    final ClipCalculate c = info.mScaleType == ScaleType.FIT_START ? new START() : info.mScaleType == ScaleType.FIT_END ? new END() : new OTHER();
    
                    postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            mTranslate.withClip(1, 1, -1 + cx, -1 + cy, mAnimaDuring / 2, c);
                        }
                    }, mAnimaDuring / 2);
                }
    
                mCompleteCallBack = completeCallBack;
                mTranslate.start();
            }
        }
    
        public void rotate(float degrees) {
            mDegrees += degrees;
            int centerX = (int) (mWidgetRect.left + mWidgetRect.width() / 2);
            int centerY = (int) (mWidgetRect.top + mWidgetRect.height() / 2);
    
            mAnimaMatrix.postRotate(degrees, centerX, centerY);
            executeTranslate();
        }
    }
    

    2、info

    
    
    import android.graphics.PointF;
    import android.graphics.RectF;
    import android.widget.ImageView;
    
    
    public class Info {
        // 内部图片在整个手机界面的位置
        RectF mRect = new RectF();
    
        // 控件在窗口的位置
        RectF mImgRect = new RectF();
    
        RectF mWidgetRect = new RectF();
    
        RectF mBaseRect = new RectF();
    
        PointF mScreenCenter = new PointF();
    
        float mScale;
    
        float mDegrees;
    
        ImageView.ScaleType mScaleType;
    
        public Info(RectF rect, RectF img, RectF widget, RectF base, PointF screenCenter, float scale, float degrees, ImageView.ScaleType scaleType) {
            mRect.set(rect);
            mImgRect.set(img);
            mWidgetRect.set(widget);
            mScale = scale;
            mScaleType = scaleType;
            mDegrees = degrees;
            mBaseRect.set(base);
            mScreenCenter.set(screenCenter);
        }
    }
    

    3、RotateGestureDetector

    import android.view.MotionEvent;
    
    /**
     * describe:图片查看器
     */
    
    public class RotateGestureDetector {
    
        private static final int MAX_DEGREES_STEP = 120;
    
        private OnRotateListener mListener;
    
        private float mPrevSlope;
        private float mCurrSlope;
    
        private float x1;
        private float y1;
        private float x2;
        private float y2;
    
        public RotateGestureDetector(OnRotateListener l) {
            mListener = l;
        }
    
        public void onTouchEvent(MotionEvent event) {
    
            final int Action = event.getActionMasked();
    
            switch (Action) {
                case MotionEvent.ACTION_POINTER_DOWN:
                case MotionEvent.ACTION_POINTER_UP:
                    if (event.getPointerCount() == 2) mPrevSlope = caculateSlope(event);
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (event.getPointerCount() > 1) {
                        mCurrSlope = caculateSlope(event);
    
                        double currDegrees = Math.toDegrees(Math.atan(mCurrSlope));
                        double prevDegrees = Math.toDegrees(Math.atan(mPrevSlope));
    
                        double deltaSlope = currDegrees - prevDegrees;
    
                        if (Math.abs(deltaSlope) <= MAX_DEGREES_STEP) {
                            mListener.onRotate((float) deltaSlope, (x2 + x1) / 2, (y2 + y1) / 2);
                        }
                        mPrevSlope = mCurrSlope;
                    }
                    break;
                default:
                    break;
            }
        }
    
        private float caculateSlope(MotionEvent event) {
            x1 = event.getX(0);
            y1 = event.getY(0);
            x2 = event.getX(1);
            y2 = event.getY(1);
            return (y2 - y1) / (x2 - x1);
        }
    }
    
    interface OnRotateListener {
        void onRotate(float degrees, float focusX, float focusY);
    }

    使用:

    在xml中直接使用

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:background="#000000">
     
        <com.example.qd.photolook.utils.PhotoView
            android:id="@+id/photoView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerInside"/>
    </LinearLayout>

    最后再对应的 Activity 中初始化控件,调用 enable() 方法即可:

    public class MainActivity extends AppCompatActivity {
        private PhotoView photoView;
     
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            photoView = findViewById(R.id.photoView);
     
            //调用方法
            photoView.enable();
        }
    }

     

    展开全文
  • 这是我的第一篇博客,写的不好请大家见谅,能帮到你是最好的,不能帮到你请你也...但是手势缩放往往会束手无策,接下来我有一个特别简单的方法,只需要导进去一个js插件就行啦。  https://github.com/webjyh/MPrev

          这是我的第一篇博客,写的不好请大家见谅,能帮到你是最好的,不能帮到你请你也了解一下微笑微笑微笑

          我们的安卓程序有时候是基于webview用js和h5来编写的,那么图片支持双击放大、缩小是可以做到的;但是手势缩放往往会束手无策,接下来我有一个特别简单的方法,只需要导进去一个js插件就行啦。

                          https://github.com/webjyh/MPreview.mobile下载插件的网址

                                                    下载下来的js文件夹中有3个js

         如果引用的事‘Mpreview.mobile.min.js’则无需引用手势库‘quo.js’,因为‘Mpreview.mobile.min.js’打包是一并把‘quo.js’打包进来了。

            你只需要把Mpreview.mobile.js和quo.js或Mpreview.mobile.min.js导入到你写的h5的程序中即可:

            

                 这样就大功告成啦~~~你把写好的js,css,html再放入你的安卓程序中,你的图片就可以支持手势缩放啦!

             万事开头难,第一篇博客,希望能帮到你


    展开全文
  • 支持手势缩放的ImageView

    千次阅读 2016-08-31 16:52:31
    前两篇文章讲解了Android的触控机制和手势操作,讲到多点触控时我们举了一个简单的手势缩放图片的例子,但是功能很有限。本篇我们通过解析一个国外大牛Mike Ortiz写的自定义TouchImageView的源码,来更加深入了解...
  • 先简单说下PhotoView的作用:不需要写任何代码就可以实现图片手势缩放,旋转,相比ImageView,用户体验更好。 先放下实现效果图。 第一步先导包,在build.gradle(app)中的dependencies中添加如下内容: ...
  • 通过手势实现的缩放处理 .rar,太多无法一一验证是否可用,程序如果跑不起来需要自调,部分代码功能进行参考学习。
  • 【实例简介】Android仿微信朋友圈图片浏览器(支持图片手势缩放,拖动)【实例截图】【核心代码】ImageDemo-2014-02-18└── ImageDemo-2014-02-18├── AndroidManifest.xml├── bin│ ├── AndroidManifest....
  • 使用了PhotoSwipe出现了点问题:在安卓手机中,可以实现双击缩放,但无法识别手势缩放,查阅一些资料后发现是(allowRotationOnUserZoom: 只有 iOS 支持) 不知道各位大神有何解决方案或者更好的实现途径? 谢谢大家!
  • ImageView通过matrix实现手势缩放

    千次阅读 2014-04-09 01:20:00
    本文章来给各位同学介绍一下Android开发之ImageView通过matrix实现手势缩放操作方法,我们知道安卓中ImageView本身有scaleType属性,通过设置android:scaleType=matrix 可以用很少的代码就实现缩放功能了,下面我们...
  • 自己封装的安卓手势识别器工具类,这个demo是使用这个手势识别器实现的超级图片ImageView控件,支持单指点击、双击、长按、移动;支持双指缩放、旋转、平移等操作。   代码很简单,...
  • (Android平台)可以根据手势简单缩放图片.rar,太多无法一一验证是否可用,程序如果跑不起来需要自调,部分代码功能进行参考学习。
  • 推荐安卓开发神器(里面有各种UI特效和android代码库实例) SDK2.1   将以下代码写到MulitPointTouchListener .java中,然后对你相应的图片进行OnTouchListener比如:imageView.setOnTouchListener(new ...
  • Android 缩放手势检测,ScaleGestureDetector 相关内容的用法和注意事项,本文依旧属于事件处理这一体系,在大多数的情况下,缩放手势都不是单独存在的,需要配合其它的手势来使用,所以推荐配合 手势检测...
  • //实例化手势侦测器(参数1:上下文环境,参数2:手势监听器对象) detector=new GestureDetector(this,this); } /** * 将窗口触摸事件交给手势侦测器处理 * */ public boolean onTouchEvent(MotionEvent ...
  • 对于新手来说怎么写个缩放手势算法可能是个有点上头的问题,这里我介绍一下我写的一个缩放算法逻辑,感觉挺清晰明了,你只需要还记得高中的一些三角函数与平面坐标系知识就够了。言归正传先上代码。 private void ...
  • 安卓实现图片的缩放

    2019-10-03 03:45:00
    借用一个第三方控件实现安卓app中的图片缩放 PhotoView的简介:这是一个图片查看库,实现图片浏览功能,支持pinch(捏合)手势或者点击放大缩小。支持在ViewPager中翻页浏览图片。 PhotoView 是一款扩展自...
  • 切换图片(通过普通按钮或者手势滑动) 缩放图片(通过缩放按钮ZoomControls来实现) 1.将图片素材导入到drawable包中 2.编辑activity_main.xml布局资源文件 通过缩放按钮ZoomControls来实现 完整代码如下: <?...
  • 仿酷狗菜单栏侧滑效果,支持手势快速滑动打开或者关闭,打开或关闭时有阴影和缩放效果.rar,太多无法一一验证是否可用,程序如果跑不起来需要自调,部分代码功能进行参考学习。
  • 学习安卓手势滑动,多点触摸放大缩小图片,分享给大家供大家参考,具体代码如下 1.布局文件如下main.xml <?xml version=1.0 encoding=utf-8?> android:layout_width=fill_parent android:layout_height=...
  • 推荐安卓开发神器(里面有各种UI特效和android代码库实例) 原文链接:http://www.iteye.com/topic/516876原文《我认为HTC HERO-ROM支持所谓的多点触摸是个谎言》我觉得太具有攻击性,和我平时的风格不大相符,故...
  • Android 手势检测,主要是 GestureDetector ...在开发 Android 手机应用过程中,可能需要对一些手势作出响应,如:单击、双击、长按、滑动、缩放等。这些都是很常用的手势。就拿最简单的双击来说吧,假如我们需要...
  • 关于Vuforia sdk的安装,unity的安卓环境配置和打包apk,网上教程很多,这里就不介绍了,主要放一下虚拟按钮和手势控制的代码,供大家参考学习。 我的unity版本是2019.4.24f1c1,Vuforia sdk版本是目前最新版9.8.5 ...
  • Android 手势检测,主要是 GestureDetector ...在开发 Android 手机应用过程中,可能需要对一些手势作出响应,如:单击、双击、长按、滑动、缩放等。这些都是很常用的手势。就拿最简单的双击来说吧,假如我们需要判断一
  • 主要是我不知道怎样实现以下效果:这个东西叫时间轴,是用在一个叫“历史地图”的安卓App 上,实现的功能有 1,可拖动 2,这个轴可以通过手势操作进行标度改变,比如两点触控可导致这个时间轴上时间跨度放大和...

空空如也

空空如也

1 2 3 4
收藏数 70
精华内容 28
关键字:

安卓手势缩放