Code example for ImageView

Methods: setImageMatrix

0
 
		if (!imageView.isInEditMode()) {
			// Create Gesture Detectors... 
			mScaleDragDetector = VersionedGestureDetector.newInstance(imageView.getContext(), this);
 
			mGestureDetector = new GestureDetector(imageView.getContext(),
					new GestureDetector.SimpleOnGestureListener() {
 
						// forward long click listener 
						@Override 
						public void onLongPress(MotionEvent e) {
							if(null != mLongClickListener) {
								mLongClickListener.onLongClick(mImageView.get());
							} 
						}}); 
 
			mGestureDetector.setOnDoubleTapListener(this);
 
			// Finally, update the UI so that we're zoomable 
			setZoomable(true); 
		} 
	} 
 
	@Override 
	public final boolean canZoom() { 
		return mZoomEnabled;
	} 
 
	/** 
	 * Clean-up the resources attached to this object. This needs to be called 
	 * when the ImageView is no longer used. A good example is from 
	 * {@link android.view.View#onDetachedFromWindow()} or from {@link android.app.Activity#onDestroy()}. 
	 * This is automatically called if you are using {@link uk.co.senab.photoview.PhotoView}. 
	 */ 
	@SuppressWarnings("deprecation") 
	public final void cleanup() { 
		if (null != mImageView) {
			mImageView.get().getViewTreeObserver().removeGlobalOnLayoutListener(this);
		} 
		mViewTreeObserver = null;
 
		// Clear listeners too 
		mMatrixChangeListener = null;
		mPhotoTapListener = null;
		mViewTapListener = null;
 
		// Finally, clear ImageView 
		mImageView = null;
	} 
 
	@Override 
	public final RectF getDisplayRect() {
		checkMatrixBounds(); 
		return getDisplayRect(getDisplayMatrix()); 
	} 
 
	public final ImageView getImageView() {
		ImageView imageView = null;
 
		if (null != mImageView) {
			imageView = mImageView.get();
		} 
 
		// If we don't have an ImageView, call cleanup() 
		if (null == imageView) {
			cleanup(); 
			throw new IllegalStateException(
					"ImageView no longer exists. You should not use this PhotoViewAttacher any more."); 
		} 
 
		return imageView;
	} 
 
	@Override 
	public float getMinScale() { 
		return mMinScale;
	} 
 
	@Override 
	public float getMidScale() { 
		return mMidScale;
	} 
 
	@Override 
	public float getMaxScale() { 
		return mMaxScale;
	} 
 
	@Override 
	public final float getScale() { 
		return getValue(mSuppMatrix, Matrix.MSCALE_X);
	} 
 
	@Override 
	public final ScaleType getScaleType() {
		return mScaleType;
	} 
 
	public final boolean onDoubleTap(MotionEvent ev) {
		try { 
			float scale = getScale();
			float x = ev.getX();
			float y = ev.getY();
 
			if (scale < mMidScale) {
				zoomTo(mMidScale, x, y);
			} else if (scale >= mMidScale && scale < mMaxScale) {
				zoomTo(mMaxScale, x, y);
			} else { 
				zoomTo(mMinScale, x, y);
			} 
		} catch (ArrayIndexOutOfBoundsException e) {
			// Can sometimes happen when getX() and getY() is called 
		} 
 
		return true; 
	} 
 
	public final boolean onDoubleTapEvent(MotionEvent e) {
		// Wait for the confirmed onDoubleTap() instead 
		return false; 
	} 
 
	public final void onDrag(float dx, float dy) {
		if (DEBUG) {
			Log.d(LOG_TAG, String.format("onDrag: dx: %.2f. dy: %.2f", dx, dy));
		} 
 
		ImageView imageView = getImageView();
 
		if (null != imageView && hasDrawable(imageView)) {
			mSuppMatrix.postTranslate(dx, dy);
			checkAndDisplayMatrix(); 
 
			/** 
			 * Here we decide whether to let the ImageView's parent to start 
			 * taking over the touch event. 
			 * 
			 * First we check whether this function is enabled. We never want the 
             * parent to take over if we're scaling. We then check the edge we're 
             * on, and the direction of the scroll (i.e. if we're pulling against 
             * the edge, aka 'overscrolling', let the parent take over). 
			 */ 
			if (mAllowParentInterceptOnEdge && !mScaleDragDetector.isScaling()) {
				if (mScrollEdge == EDGE_BOTH || (mScrollEdge == EDGE_LEFT && dx >= 1f)
						|| (mScrollEdge == EDGE_RIGHT && dx <= -1f)) {
					imageView.getParent().requestDisallowInterceptTouchEvent(false);
				} 
			} 
		} 
	} 
 
	@Override 
	public final void onFling(float startX, float startY, float velocityX, float velocityY) {
		if (DEBUG) {
			Log.d(LOG_TAG, "onFling. sX: " + startX + " sY: " + startY + " Vx: " + velocityX + " Vy: " + velocityY);
		} 
 
		ImageView imageView = getImageView();
		if (hasDrawable(imageView)) {
			mCurrentFlingRunnable = new FlingRunnable(imageView.getContext());
			mCurrentFlingRunnable.fling(imageView.getWidth(), imageView.getHeight(), (int) velocityX, (int) velocityY);
			imageView.post(mCurrentFlingRunnable);
		} 
	} 
 
	@Override 
	public final void onGlobalLayout() { 
		ImageView imageView = getImageView();
 
		if (null != imageView && mZoomEnabled) {
			final int top = imageView.getTop();
			final int right = imageView.getRight();
			final int bottom = imageView.getBottom();
			final int left = imageView.getLeft();
 
			/** 
			 * We need to check whether the ImageView's bounds have changed. 
			 * This would be easier if we targeted API 11+ as we could just use 
			 * View.OnLayoutChangeListener. Instead we have to replicate the 
			 * work, keeping track of the ImageView's bounds and then checking 
			 * if the values change. 
			 */ 
			if (top != mIvTop || bottom != mIvBottom || left != mIvLeft || right != mIvRight) {
				// Update our base matrix, as the bounds have changed 
				updateBaseMatrix(imageView.getDrawable());
 
				// Update values as something has changed 
				mIvTop = top;
				mIvRight = right;
				mIvBottom = bottom;
				mIvLeft = left;
			} 
		} 
	} 
 
	public final void onScale(float scaleFactor, float focusX, float focusY) {
		if (DEBUG) {
			Log.d(LOG_TAG, String.format("onScale: scale: %.2f. fX: %.2f. fY: %.2f", scaleFactor, focusX, focusY));
		} 
 
		if (hasDrawable(getImageView()) && (getScale() < mMaxScale || scaleFactor < 1f)) {
			mSuppMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY);
			checkAndDisplayMatrix(); 
		} 
	} 
 
	public final boolean onSingleTapConfirmed(MotionEvent e) {
		ImageView imageView = getImageView();
 
		if (null != imageView) {
			if (null != mPhotoTapListener) {
				final RectF displayRect = getDisplayRect();
 
				if (null != displayRect) {
					final float x = e.getX(), y = e.getY();
 
					// Check to see if the user tapped on the photo 
					if (displayRect.contains(x, y)) {
 
						float xResult = (x - displayRect.left) / displayRect.width();
						float yResult = (y - displayRect.top) / displayRect.height();
 
						mPhotoTapListener.onPhotoTap(imageView, xResult, yResult);
						return true; 
					} 
				} 
			} 
			if (null != mViewTapListener) {
				mViewTapListener.onViewTap(imageView, e.getX(), e.getY());
			} 
		} 
 
		return false; 
	} 
 
	@Override 
	public final boolean onTouch(View v, MotionEvent ev) {
		boolean handled = false;
 
		if (mZoomEnabled) {
			switch (ev.getAction()) {
				case MotionEvent.ACTION_DOWN:
					// First, disable the Parent from intercepting the touch 
					// event 
					v.getParent().requestDisallowInterceptTouchEvent(true);
 
					// If we're flinging, and the user presses down, cancel 
					// fling 
					cancelFling(); 
					break; 
 
				case MotionEvent.ACTION_CANCEL:
				case MotionEvent.ACTION_UP:
					// If the user has zoomed less than min scale, zoom back 
					// to min scale 
					if (getScale() < mMinScale) {
						RectF rect = getDisplayRect();
						if (null != rect) {
							v.post(new AnimatedZoomRunnable(getScale(), mMinScale, rect.centerX(), rect.centerY()));
							handled = true;
						} 
					} 
					break; 
			} 
 
			// Check to see if the user double tapped 
			if (null != mGestureDetector && mGestureDetector.onTouchEvent(ev)) {
				handled = true;
			} 
 
			// Finally, try the Scale/Drag detector 
			if (null != mScaleDragDetector && mScaleDragDetector.onTouchEvent(ev)) {
				handled = true;
			} 
		} 
 
		return handled;
	} 
 
    @Override 
    public void setAllowParentInterceptOnEdge(boolean allow) {
        mAllowParentInterceptOnEdge = allow;
    } 
 
	@Override 
	public void setMinScale(float minScale) {
		checkZoomLevels(minScale, mMidScale, mMaxScale);
		mMinScale = minScale;
	} 
 
	@Override 
	public void setMidScale(float midScale) {
		checkZoomLevels(mMinScale, midScale, mMaxScale);
		mMidScale = midScale;
	} 
 
	@Override 
	public void setMaxScale(float maxScale) {
		checkZoomLevels(mMinScale, mMidScale, maxScale);
		mMaxScale = maxScale;
	} 
 
	@Override 
	public final void setOnLongClickListener(OnLongClickListener listener) {
		mLongClickListener = listener;
	} 
 
	@Override 
	public final void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
		mMatrixChangeListener = listener;
	} 
 
	@Override 
	public final void setOnPhotoTapListener(OnPhotoTapListener listener) {
		mPhotoTapListener = listener;
	} 
 
	@Override 
	public final void setOnViewTapListener(OnViewTapListener listener) {
		mViewTapListener = listener;
	} 
 
	@Override 
	public final void setScaleType(ScaleType scaleType) {
		if (isSupportedScaleType(scaleType) && scaleType != mScaleType) {
			mScaleType = scaleType;
 
			// Finally update 
			update(); 
		} 
	} 
 
	@Override 
	public final void setZoomable(boolean zoomable) {
		mZoomEnabled = zoomable;
		update(); 
	} 
 
	public final void update() { 
		ImageView imageView = getImageView();
 
		if (null != imageView) {
			if (mZoomEnabled) {
				// Make sure we using MATRIX Scale Type 
				setImageViewScaleTypeMatrix(imageView);
 
				// Update the base matrix using the current drawable 
				updateBaseMatrix(imageView.getDrawable());
			} else { 
				// Reset the Matrix... 
				resetMatrix(); 
			} 
		} 
	} 
 
	@Override 
	public final void zoomTo(float scale, float focalX, float focalY) {
		ImageView imageView = getImageView();
 
		if (null != imageView) {
			imageView.post(new AnimatedZoomRunnable(getScale(), scale, focalX, focalY));
		} 
	} 
 
	protected Matrix getDisplayMatrix() {
		mDrawMatrix.set(mBaseMatrix);
		mDrawMatrix.postConcat(mSuppMatrix);
		return mDrawMatrix;
	} 
 
	private void cancelFling() { 
		if (null != mCurrentFlingRunnable) {
			mCurrentFlingRunnable.cancelFling();
			mCurrentFlingRunnable = null;
		} 
	} 
 
	/** 
	 * Helper method that simply checks the Matrix, and then displays the result 
	 */ 
	private void checkAndDisplayMatrix() { 
		checkMatrixBounds(); 
		setImageViewMatrix(getDisplayMatrix()); 
	} 
 
	private void checkImageViewScaleType() { 
		ImageView imageView = getImageView();
 
		/** 
		 * PhotoView's getScaleType() will just divert to this.getScaleType() so 
		 * only call if we're not attached to a PhotoView. 
		 */ 
		if (null != imageView && !(imageView instanceof PhotoView)) {
			if (imageView.getScaleType() != ScaleType.MATRIX) {
				throw new IllegalStateException(
						"The ImageView's ScaleType has been changed since attaching a PhotoViewAttacher"); 
			} 
		} 
	} 
 
	private void checkMatrixBounds() { 
		final ImageView imageView = getImageView();
		if (null == imageView) {
			return; 
		} 
 
		final RectF rect = getDisplayRect(getDisplayMatrix());
		if (null == rect) {
			return; 
		} 
 
		final float height = rect.height(), width = rect.width();
		float deltaX = 0, deltaY = 0;
 
		final int viewHeight = imageView.getHeight();
		if (height <= viewHeight) {
			switch (mScaleType) {
				case FIT_START:
					deltaY = -rect.top;
					break; 
				case FIT_END:
					deltaY = viewHeight - height - rect.top;
					break; 
				default: 
					deltaY = (viewHeight - height) / 2 - rect.top;
					break; 
			} 
		} else if (rect.top > 0) {
			deltaY = -rect.top;
		} else if (rect.bottom < viewHeight) {
			deltaY = viewHeight - rect.bottom;
		} 
 
		final int viewWidth = imageView.getWidth();
		if (width <= viewWidth) {
			switch (mScaleType) {
				case FIT_START:
					deltaX = -rect.left;
					break; 
				case FIT_END:
					deltaX = viewWidth - width - rect.left;
					break; 
				default: 
					deltaX = (viewWidth - width) / 2 - rect.left;
					break; 
			} 
			mScrollEdge = EDGE_BOTH;
		} else if (rect.left > 0) {
			mScrollEdge = EDGE_LEFT;
			deltaX = -rect.left;
		} else if (rect.right < viewWidth) {
			deltaX = viewWidth - rect.right;
			mScrollEdge = EDGE_RIGHT;
		} else { 
			mScrollEdge = EDGE_NONE;
		} 
 
		// Finally actually translate the matrix 
		mSuppMatrix.postTranslate(deltaX, deltaY);
	} 
 
	/** 
	 * Helper method that maps the supplied Matrix to the current Drawable 
	 *  
	 * @param matrix - Matrix to map Drawable against 
	 * @return RectF - Displayed Rectangle 
	 */ 
	private RectF getDisplayRect(Matrix matrix) {
		ImageView imageView = getImageView();
 
		if (null != imageView) {
			Drawable d = imageView.getDrawable();
			if (null != d) {
				mDisplayRect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
				matrix.mapRect(mDisplayRect);
				return mDisplayRect;
			} 
		} 
		return null; 
	} 
 
	/** 
	 * Helper method that 'unpacks' a Matrix and returns the required value 
	 *  
	 * @param matrix - Matrix to unpack 
	 * @param whichValue - Which value from Matrix.M* to return 
	 * @return float - returned value 
	 */ 
	private float getValue(Matrix matrix, int whichValue) {
		matrix.getValues(mMatrixValues);
		return mMatrixValues[whichValue];
	} 
 
	/** 
	 * Resets the Matrix back to FIT_CENTER, and then displays it.s 
	 */ 
	private void resetMatrix() { 
		mSuppMatrix.reset();
		setImageViewMatrix(getDisplayMatrix()); 
		checkMatrixBounds(); 
	} 
 
	private void setImageViewMatrix(Matrix matrix) {
		ImageView imageView = getImageView();
		if (null != imageView) {
 
			checkImageViewScaleType(); 
			imageView.setImageMatrix(matrix);
 
			// Call MatrixChangedListener if needed 
			if (null != mMatrixChangeListener) {
				RectF displayRect = getDisplayRect(matrix);
				if (null != displayRect) {
					mMatrixChangeListener.onMatrixChanged(displayRect);
				} 
			} 
		} 
	} 
 
	/** 
	 * Calculate Matrix for FIT_CENTER 
	 *  
	 * @param d - Drawable being displayed 
	 */ 
	private void updateBaseMatrix(Drawable d) {
		ImageView imageView = getImageView();
		if (null == imageView || null == d) {
			return; 
		} 
 
		final float viewWidth = imageView.getWidth();
		final float viewHeight = imageView.getHeight();
		final int drawableWidth = d.getIntrinsicWidth();
		final int drawableHeight = d.getIntrinsicHeight();
 
		mBaseMatrix.reset();
 
		final float widthScale = viewWidth / drawableWidth;
		final float heightScale = viewHeight / drawableHeight;
 
		if (mScaleType == ScaleType.CENTER) {
			mBaseMatrix.postTranslate((viewWidth - drawableWidth) / 2F, (viewHeight - drawableHeight) / 2F);
 
		} else if (mScaleType == ScaleType.CENTER_CROP) {
			float scale = Math.max(widthScale, heightScale);
			mBaseMatrix.postScale(scale, scale);
			mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
					(viewHeight - drawableHeight * scale) / 2F);
 
		} else if (mScaleType == ScaleType.CENTER_INSIDE) {
			float scale = Math.min(1.0f, Math.min(widthScale, heightScale));
			mBaseMatrix.postScale(scale, scale);
			mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
					(viewHeight - drawableHeight * scale) / 2F);
 
		} else { 
			RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight);
			RectF mTempDst = new RectF(0, 0, viewWidth, viewHeight);
 
			switch (mScaleType) {
				case FIT_CENTER:
					mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.CENTER);