大佬教程收集整理的这篇文章主要介绍了Android 自定义imageview实现图片缩放实例详解,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
Android 自定义imageview实现图片缩放实例详解
觉得这个自定义的imageview很好用 性能不错 所以拿出来分享给大家 因为不会做gif图 所以项目效果 就不好贴出来了 把代码贴出来
1.项目结构图
2.Compat.class
package com.suo.image; import android.os.build.VERSION; import android.os.build.VERSION_CODES; import android.view.View; public class Compat { private static final int SIXTY_FPS_INTERVAL = 1000 / 60; public static void postOnAnimation(View view,Runnable runnablE) { if (VERSION.SDK_INT >= VERSION_CODEs.jeLLY_BEAN) { SDK16.postOnAnimation(view,runnablE); } else { view.postDelayed(runnable,SIXTY_FPS_INTERVAL); } } }@H_502_24@
3.HackyViewPager.class
package com.suo.image; import android.content.Context; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; /** * Hacky fix for Issue #4 and * http://code.google.com/p/android/issues/detail?id=18990 * * ScaleGestureDetector seems to mess up the touch events,which means that * ViewGroups which make use of onInterceptTouchEvent throw a lot of * IllegalArgumentexception: pointerIndex out of range. * * There's not much I can do in my code for Now,but we can mask the result by * just catching the problem and ignoring it. * * @author Chris Banes */ public class HackyViewPager extends ViewPager { public HackyViewPager(Context context) { super(context); } public HackyViewPager(Context context,AttributeSet attrs) { super(context,attrs); // TODO Auto-generated constructor stub } @Override public Boolean onInterceptTouchEvent(MotionEvent ev) { try { return super.onInterceptTouchEvent(ev); } catch (IllegalArgumentexception E) { e.printStackTrace(); return false; } } }@H_502_24@
4.IScaleView.class
/******************************************************************************* * Copyright 2011,2012 Chris Banes. * * Licensed under the Apache License,Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in wriTing,software * diStributed under the License is diStributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.suo.image; import android.graphics.RectF; import android.view.View; import android.widget.ImageView; public interface IScaleView { /** * Returns true if the ScaleView is set to allow zooming of Scales. * * @return true if the ScaleView allows zooming. */ Boolean canZoom(); /** * Gets the Display Rectangle of the currently displayed Drawable. The * Rectangle is relative to this View and includes all scaling and * translations. * * @return - RectF of Displayed Drawable */ RectF getDisplayRect(); /** * @return The current minimum scale level. what this value represents depends on the current {@link android.widget.ImageView.ScaleTypE}. */ float getMinScale(); /** * @return The current middle scale level. what this value represents depends on the current {@link android.widget.ImageView.ScaleTypE}. */ float getMidScale(); /** * @return The current maximum scale level. what this value represents depends on the current {@link android.widget.ImageView.ScaleTypE}. */ float getMaxScale(); /** * Returns the current scale value * * @return float - current scale value */ float getScale(); /** * Return the current scale type in use by the ImageView. */ ImageView.ScaleType getScaleType(); /** * Whether to allow the ImageView's parent to intercept the touch event when the Scale is scroll to it's horizontal edge. */ void setAllowParenTinterceptOnEdge(Boolean allow); /** * Sets the minimum scale level. what this value represents depends on the current {@link android.widget.ImageView.ScaleTypE}. */ void setMinScale(float minScalE); /** * Sets the middle scale level. what this value represents depends on the current {@link android.widget.ImageView.ScaleTypE}. */ void setMidScale(float midScalE); /** * Sets the maximum scale level. what this value represents depends on the current {@link android.widget.ImageView.ScaleTypE}. */ void setMaxScale(float maxScalE); /** * Register a callBACk to be invoked when the Scale displayed by this view is long-pressed. * * @param listener - Listener to be registered. */ void setOnLongClickListener(View.onLongClickListener listener); /** * Register a callBACk to be invoked when the Matrix has changed for this * View. An example would be the user pAnning or scaling the Scale. * * @param listener - Listener to be registered. */ void setOnMatrixchangelistener(ScaleViewAttacher.onMatrixChangedListener listener); /** * Register a callBACk to be invoked when the Scale displayed by this View * is tapped with a single tap. * * @param listener - Listener to be registered. */ void setOnScaleTapListener(ScaleViewAttacher.onScaleTapListener listener); /** * Register a callBACk to be invoked when the View is tapped with a single * tap. * * @param listener - Listener to be registered. */ void setOnViewTapListener(ScaleViewAttacher.onViewTapListener listener); /** * Controls how the image should be resized or moved to match the size of * the ImageView. Any scaling or pAnning will happen within the confines of * this {@link android.widget.ImageView.ScaleTypE}. * * @param scaleType - The desired scaling mode. */ void setScaleType(ImageView.ScaleType scaleTypE); /** * Allows you to enable/disable the zoom functionality on the ImageView. * When disable the ImageView reverts to using the FIT_CENTER matrix. * * @param zoomable - Whether the zoom functionality is enabled. */ void setZoomable(Boolean zoomablE); /** * Zooms to the specified scale,around the focal point given. * * @param scale - Scale to zoom to * @param focalX - X Focus Point * @param focalY - Y Focus Point */ void zoomTo(float scale,float focalX,float focalY); }@H_502_24@
5.ScaleView
/******************************************************************************* * Copyright 2011,either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.suo.image; import com.suo.image.ScaleViewAttacher.onMatrixChangedListener; import com.suo.image.ScaleViewAttacher.onScaleTapListener; import com.suo.image.ScaleViewAttacher.onViewTapListener; import android.content.Context; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.net.Uri; import android.util.AttributeSet; import android.widget.ImageView; public class ScaleView extends ImageView implements IScaleView { @SuppressWarnings("unused") private static final String TAG = "ScaleView"; private final ScaleViewAttacher mAttacher; private ScaleType mPendingScaleType; public ScaleView(Context context) { this(context,null); setZoomable(false); } public ScaleView(Context context,AttributeSet attr) { this(context,attr,0); } public ScaleView(Context context,AttributeSet attr,int defStylE) { super(context,defStylE); super.setScaleType(ScaleType.MATRIX); mAttacher = new ScaleViewAttacher(this); if (null != mPendingScaleTypE) { setScaleType(mPendingScaleTypE); mPendingScaleType = null; } } public void setOnClickListener(OnClickListener listener){ mAttacher.setOnClickLinstener(listener); } @Override public Boolean canZoom() { return mAttacher.canZoom(); } @Override public RectF getDisplayRect() { return mAttacher.getDisplayRect(); } @Override public float getMinScale() { return mAttacher.getMinScale(); } @Override public float getMidScale() { return mAttacher.getMidScale(); } @Override public float getMaxScale() { return mAttacher.getMaxScale(); } @Override public float getScale() { return mAttacher.getScale(); } @Override public ScaleType getScaleType() { return mAttacher.getScaleType(); } @Override public void setAllowParenTinterceptOnEdge(Boolean allow) { mAttacher.setAllowParenTinterceptOnEdge(allow); } @Override public void setMinScale(float minScalE) { mAttacher.setMinScale(minScalE); } @Override public void setMidScale(float midScalE) { mAttacher.setMidScale(midScalE); } @Override public void setMaxScale(float maxScalE) { mAttacher.setMaxScale(maxScalE); } @Override // setImageBitmap calls through to this method public void setImageDrawable(Drawable drawablE) { super.setImageDrawable(drawablE); if (null != mAttacher) { mAttacher.update(); } } @Override public void setImageresource(int resId) { super.setImageresource(resId); if (null != mAttacher) { mAttacher.update(); } } @Override public void setImageURI(Uri uri) { super.setImageURI(uri); if (null != mAttacher) { mAttacher.update(); } } @Override public void setOnMatrixchangelistener(OnMatrixChangedListener listener) { mAttacher.setOnMatrixchangelistener(listener); } @Override public void setOnLongClickListener(OnLongClickListener l) { mAttacher.setOnLongClickListener(l); } @Override public void setOnScaleTapListener(OnScaleTapListener listener) { mAttacher.setOnScaleTapListener(listener); } @Override public void setOnViewTapListener(OnViewTapListener listener) { mAttacher.setOnViewTapListener(listener); } @Override public void setScaleType(ScaleType scaleTypE) { if (null != mAttacher) { mAttacher.setScaleType(scaleTypE); } else { mPendingScaleType = scaleType; } } @Override public void setZoomable(Boolean zoomablE) { mAttacher.setZoomable(zoomablE); } @Override public void zoomTo(float scale,float focalY) { mAttacher.zoomTo(scale,focalX,focalY); } @Override protected void onDetachedFromWindow() { mAttacher.cleanup(); super.onDetachedFromWindow(); } }@H_502_24@
6.ScaleViewAttacher 这个是最关键的
/******************************************************************************* * Copyright 2011,either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.suo.image; import android.content.Context; import android.graphics.Matrix; import android.graphics.Matrix.ScaleToFit; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.View.onClickListener; import android.view.View.onLongClickListener; import android.view.ViewTreeObserver; import android.widget.ImageView; import android.widget.ImageView.ScaleType; import java.lang.ref.WeakReference; public class ScaleViewAttacher implements IScaleView,View.onTouchListener,VersionedGestureDetector.onGestureListener,GestureDetector.onDoubleTapListener,ViewTreeObserver.onGlobalLayoutListener { static final String LOG_TAG = "ScaleViewAttacher"; // let debug flag be dynamic,but still Proguard can be used to remove from release builds static final Boolean DEBUG = Log.isLoggable(LOG_TAG,Log.DEBUG); static final int EDGE_NONE = -1; static final int EDGE_LEFT = 0; static final int EDGE_RIGHT = 1; static final int EDGE_BOTH = 2; public static final float DEFAULT_MAX_SCALE = 3.0f; public static final float DEFAULT_MID_SCALE = 1.75f; public static final float DEFAULT_MIN_SCALE = 1.0f; private float mMinScale = DEFAULT_MIN_SCALE; private float mMidScale = DEFAULT_MID_SCALE; private float mMaxScale = DEFAULT_MAX_SCALE; private Boolean mAllowParenTinterceptOnEdge = true; private static void checkZoomLevels(float minZoom,float midZoom,float maxZoom) { if (minZoom >= midZoom) { throw new IllegalArgumentexception("MinZoom should be less than MidZoom"); } else if (midZoom >= maxZoom) { throw new IllegalArgumentexception("MidZoom should be less than MaxZoom"); } } /** * @return true if the ImageView exists,and it's Drawable existss */ private static Boolean hasDrawable(ImageView imageView) { return null != imageView && null != imageView.getDrawable(); } /** * @return true if the ScaleType is supported. */ private static Boolean isSupportedScaleType(final ScaleType scaleTypE) { if (null == scaleTypE) { return false; } switch (scaleTypE) { case MATRIX: throw new IllegalArgumentexception(scaleType.name() + " is not supported in ScaleView"); default: return true; } } /** * Set's the ImageView's ScaleType to Matrix. */ private static void setImageViewScaleTypeMatrix(ImageView imageView) { if (null != imageView) { if (imageView instanceof ScaleView) { /** * ScaleView sets it's own ScaleType to Matrix,then diverts all * calls setScaleType to this.setScaleType. Basically we don't * need to do anything here */ } else { imageView.setScaleType(ScaleType.MATRIX); } } } private WeakReference<ImageView> mImageView; private ViewTreeObserver mViewTreeObserver; // Gesture Detectors private GestureDetector mGestureDetector; private VersionedGestureDetector mScaleDragDetector; // these are set so we don't keep allocating them on the heap private final Matrix mBaseMatrix = new Matrix(); private final Matrix mDrawMatrix = new Matrix(); private final Matrix mSuppMatrix = new Matrix(); private final RectF mDisplayRect = new RectF(); private final float[] mMatrixValues = new float[9]; // Listeners private OnMatrixChangedListener mMatrixchangelistener; private OnScaleTapListener mScaleTapListener; private OnViewTapListener mViewTapListener; private OnLongClickListener mLongClickListener; privatE int mIvTop,mIvRight,mIvBottom,mIvLeft; private FlingRunnable mCurrentFlingRunnable; privatE int mScrollEdge = EDGE_BOTH; private Boolean mZoomEnabled; private ScaleType mScaleType = ScaleType.FIT_CENTER; private OnClickListener onClickListener; public ScaleViewAttacher(ImageView imageView) { mImageView = new WeakReference<ImageView>(imageView); imageView.setOnTouchListener(this); mViewTreeObserver = imageView.getViewTreeObserver(); if (mViewTreeObserver != null) { mViewTreeObserver.addOnGlobalLayoutListener(this); } onClickListener = null; // Make sure we using MATRIX Scale Type setImageViewScaleTypeMatrix(imageView); 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 ScaleView.co.senab.Scaleview.ScaleView}. */ @SuppressWarnings("deprecation") public final void cleanup() { if (null != mImageView) { android.view.ViewTreeObserver obs = mImageView.get().getViewTreeObserver(); if (obs != null) { obs.removeGlobalOnLayoutListener(this); } } mViewTreeObserver = null; // Clear listeners too mMatrixchangelistener = null; mScaleTapListener = 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 ScaleViewAttacher 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,y); } else { zoomTo(mMinScale,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)) { android.view.ViewParent vParent = imageView.getParent(); if (vParent != null) { vParent.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 setOnClickLinstener(OnClickListener listener){ onClickListener = listener; } 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,focusY); checkAndDisplaymatrix(); } } public final Boolean onSingleTapConfirmed(MotionEvent E) { ImageView imageView = getImageView(); if (null != imageView) { if (null != mScaleTapListener) { final RectF displayRect = getDisplayRect(); if (null != displayRect) { final float x = e.getX(),y = e.getY(); // check to see if the user tapped on the Scale if (displayRect.contains(x,y)) { float xResult = (x - displayRect.left) / displayRect.width(); float yResult = (y - displayRect.top) / displayRect.height(); mScaleTaplistener.onScaleTap(imageView,xResult,yResult); return true; } } } if (null != mViewTapListener) { mViewTaplistener.onViewTap(imageView,e.getX(),e.getY()); } } return false; } private float lastposx,lastposy; private long firClick = 0; @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 android.view.ViewParent vParent = v.getParent(); if (vParent != null) { vParent.requestDisallowInterceptTouchEvent(true); } lastposx = ev.getX(); lastposy = ev.getY(); // 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; } } if(ev.getX() == lastposx && ev.getY() == lastposy){ long time = System.currentTimeMillis(); if(time - firClick > 500){ firClick = System.currentTimeMillis(); if(onClickListener != null){ onClicklistener.onClick(getImageView()); } } } 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,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 setOnScaleTapListener(OnScaleTapListener listener) { mScaleTapListener = 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 focalY) { ImageView imageView = getImageView(); if (null != imageView) { imageView.post(new AnimatedZoomRunnable(getScale(),scale,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(); /** * ScaleView's getScaleType() will just divert to this.getScaleType() so * only call if we're not attached to a ScaleView. */ if (null != imageView && !(imageView instanceof ScaleView)) { if (imageView.getScaleType() != ScaleType.MATRIX) { throw new IllegalStateException( "The ImageView's ScaleType has been changed since attaching a ScaleViewAttacher"); } } } 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,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,(viewHeight - drawableHeight * scalE) / 2F); } else { RectF mTempSrc = new RectF(0,drawableWidth,drawableHeight); RectF mTempDst = new RectF(0,viewWidth,viewHeight); switch (mScaleTypE) { case FIT_CENTER: mBaseMatrix.setRectToRect(mTempSrc,mTempDst,ScaleToFit.CENTER); break; case FIT_START: mBaseMatrix.setRectToRect(mTempSrc,ScaleToFit.START); break; case FIT_END: mBaseMatrix.setRectToRect(mTempSrc,ScaleToFit.END); break; case FIT_XY: mBaseMatrix.setRectToRect(mTempSrc,ScaleToFit.FILL); break; default: break; } } resetMatrix(); } /** * Interface deFinition for a callBACk to be invoked when thE internal * Matrix has changed for this View. * * @author Chris Banes */ public static interface OnMatrixChangedListener { /** * CallBACk for when the Matrix displaying the Drawable has changed. * This Could be because the View's bounds have changed,or the user has * zoomed. * * @param rect - Rectangle displaying the Drawable's new bounds. */ void onMatrixChanged(RectF rect); } /** * Interface deFinition for a callBACk to be invoked when the Scale is * tapped with a single tap. * * @author Chris Banes */ public static interface OnScaleTapListener { /** * A callBACk to receive where the user taps on a Scale. you will only * receive a callBACk if the user taps on the actual Scale,tapping on * 'whitespace' will be ignored. * * @param view - View the user tapped. * @param x - where the user tapped from the of the Drawable,as * percentage of the Drawable width. * @param y - where the user tapped from the top of the Drawable,as * percentage of the Drawable height. */ void onScaleTap(View view,float x,float y); } /** * Interface deFinition for a callBACk to be invoked when the ImageView is * tapped with a single tap. * * @author Chris Banes */ public static interface OnViewTapListener { /** * A callBACk to receive where the user taps on a ImageView. you will * receive a callBACk if the user taps anywhere on the view,tapping on * 'whitespace' will not be ignored. * * @param view - View the user tapped. * @param x - where the user tapped from the left of the View. * @param y - where the user tapped from the top of the View. */ void onViewTap(View view,float y); } private class AnimatedZoomRunnable implements Runnable { // these are 'postScale' values,means they're compounded each iteration static final float ANIMATION_SCALE_PER_ITERATION_IN = 1.07f; static final float ANIMATION_SCALE_PER_ITERATION_OUT = 0.93f; private final float mFocalX,mFocalY; private final float mTargetZoom; private final float mDeltaScale; public AnimatedZoomRunnable(final float currentZoom,final float targetZoom,final float focalX,final float focalY) { mTargetZoom = targetZoom; mFocalX = focalX; mFocalY = focalY; if (currentZoom < targetZoom) { mDeltaScale = ANIMATION_SCALE_PER_ITERATION_IN; } else { mDeltaScale = ANIMATION_SCALE_PER_ITERATION_OUT; } } public void run() { ImageView imageView = getImageView(); if (null != imageView) { mSuppMatrix.postScale(mDeltaScale,mDeltaScale,mFocalX,mFocalY); checkAndDisplaymatrix(); final float currentScale = getScale(); if ((mDeltaScale > 1f && currentScale < mTargetZoom) || (mDeltaScale < 1f && mTargetZoom < currentScalE)) { // We haven't hit our target scale yet,so post ourselves // again Compat.postOnAnimation(imageView,this); } else { // We've scaled past our target zoom,so calculate the // necessary scale so we're BACk at target zoom final float delta = mTargetZoom / currentScale; mSuppMatrix.postScale(delta,delta,mFocalY); checkAndDisplaymatrix(); } } } } private class FlingRunnable implements Runnable { private final ScrollerProxy mScroller; privatE int mCurrentX,mCurrentY; public FlingRunnable(Context context) { mScroller = ScrollerProxy.getScroller(context); } public void cancelFling() { if (DEBUG) { Log.d(LOG_TAG,"Cancel Fling"); } mScroller.forceFinished(true); } public void fling(int viewWidth,int viewHeight,int veLocityX,int veLocityY) { final RectF rect = getDisplayRect(); if (null == rect) { return; } final int startX = Math.round(-rect.left); final int minX,maxX,minY,maxY; if (viewWidth < rect.width()) { minX = 0; maxX = Math.round(rect.width() - viewWidth); } else { minX = maxX = startX; } final int startY = Math.round(-rect.top); if (viewHeight < rect.height()) { minY = 0; maxY = Math.round(rect.height() - viewHeight); } else { minY = maxY = startY; } mCurrentX = startX; mCurrentY = startY; if (DEBUG) { Log.d(LOG_TAG,"fling. StartX:" + startX + " StartY:" + startY + " MaxX:" + maxX + " MaxY:" + maxY); } // If we actually can move,fling the scroller if (startX != maxX || startY != maxY) { mScroller.fling(startX,startY,veLocityX,veLocityY,minX,maxY,0); } } @Override public void run() { ImageView imageView = getImageView(); if (null != imageView && mScroller.computeScrollOffset()) { final int newX = mScroller.getCurrX(); final int newY = mScroller.getCurrY(); if (DEBUG) { Log.d(LOG_TAG,"fling run(). CurrentX:" + mCurrentX + " CurrentY:" + mCurrentY + " NewX:" + newX + " NewY:" + newY); } mSuppMatrix.postTranslate(mCurrentX - newX,mCurrentY - newY); setImageViewMatrix(getDisplaymatrix()); mCurrentX = newX; mCurrentY = newY; // Post On animation Compat.postOnAnimation(imageView,this); } } } }@H_502_24@
7.ScrollerProxy
/******************************************************************************* * Copyright 2011,either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.suo.image; import android.Annotation.TargetApi; import android.content.Context; import android.os.build.VERSION; import android.os.build.VERSION_CODES; import android.widget.overScroller; import android.widget.Scroller; public abstract class ScrollerProxy { public static ScrollerProxy getScroller(Context context) { if (VERSION.SDK_INT < VERSION_CODEs.GINGERBREAD) { return new PreGingerScroller(context); } else { return new GingerScroller(context); } } public abstract Boolean computeScrollOffset(); public abstract void fling(int startX,int startY,int veLocityY,int minX,int maxX,int minY,int maxY,int overX,int overY); public abstract void forceFinished(Boolean finished); public abstract int getCurrX(); public abstract int getCurrY(); @TargetApi(9) private static class GingerScroller extends ScrollerProxy { private OverScroller mScroller; public GingerScroller(Context context) { mScroller = new OverScroller(context); } @Override public Boolean computeScrollOffset() { return mScroller.computeScrollOffset(); } @Override public void fling(int startX,int overY) { mScroller.fling(startX,overX,overY); } @Override public void forceFinished(Boolean finished) { mScroller.forceFinished(finished); } @Override public int getCurrX() { return mScroller.getCurrX(); } @Override public int getCurrY() { return mScroller.getCurrY(); } } private static class PreGingerScroller extends ScrollerProxy { private Scroller mScroller; public PreGingerScroller(Context context) { mScroller = new Scroller(context); } @Override public Boolean computeScrollOffset() { return mScroller.computeScrollOffset(); } @Override public void fling(int startX,maxY); } @Override public void forceFinished(Boolean finished) { mScroller.forceFinished(finished); } @Override public int getCurrX() { return mScroller.getCurrX(); } @Override public int getCurrY() { return mScroller.getCurrY(); } } }@H_502_24@
8.SDK16
/******************************************************************************* * Copyright 2011,either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.suo.image; import android.Annotation.TargetApi; import android.view.View; @TargetApi(16) public class SDK16 { public static void postOnAnimation(View view,Runnable r) { view.postOnAnimation(r); } }@H_502_24@
9.VersionedGestureDetector
package com.suo.image; /******************************************************************************* * Copyright 2011,either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ import android.Annotation.TargetApi; import android.content.Context; import android.os.build; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.onScaleGestureListener; import android.view.VeLocityTracker; import android.view.ViewConfiguration; public abstract class VersionedGestureDetector { static final String LOG_TAG = "VersionedGestureDetector"; OnGestureListener mListener; public static VersionedGestureDetector newInstance(Context context,OnGestureListener listener) { final int sdkVersion = Build.VERSION.SDK_INT; VersionedGestureDetector detector = null; if (sdkVersion < Build.VERSION_CODEs.ECLAIR) { detector = new CupcakeDetector(context); } else if (sdkVersion < Build.VERSION_CODEs.FROYO) { detector = new EclairDetector(context); } else { detector = new FroyoDetector(context); } detector.mListener = listener; return detector; } public abstract Boolean onTouchEvent(MotionEvent ev); public abstract Boolean isScaling(); public static interface OnGestureListener { public void onDrag(float dx,float dy); public void onFling(float startX,float veLocityY); public void onScale(float scaleFactor,float focusY); } private static class CupcakeDetector extends VersionedGestureDetector { float mLastTouchX; float mLastTouchY; final float mTouchSlop; final float mMinimumVeLocity; public CupcakeDetector(Context context) { final ViewConfiguration configuration = ViewConfiguration.get(context); mMinimumVeLocity = configuration.getScaledMinimumFlingVeLocity(); mTouchSlop = configuration.getScaledTouchSlop(); } private VeLocityTracker mVeLocityTracker; private Boolean mIsDragging; float getActiveX(MotionEvent ev) { return ev.getX(); } float getActiveY(MotionEvent ev) { return ev.getY(); } public Boolean isScaling() { return false; } @Override public Boolean onTouchEvent(MotionEvent ev) { Boolean result = true; switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: { mVeLocityTracker = VeLocityTracker.obtain(); if (mVeLocityTracker != null) { mVeLocityTracker.addMovement(ev); } mLastTouchX = getActiveX(ev); mLastTouchY = getActiveY(ev); mIsDragging = false; break; } case MotionEvent.ACTION_MOVE: { final float x = getActiveX(ev); final float y = getActiveY(ev); final float dx = x - mLastTouchX,dy = y - mLastTouchY; if (!mIsDragging) { // Use Pythagoras to see if Drag length is larger than // touch slop mIsDragging = Math.sqrt((dx * dX) + (dy * dy)) >= mTouchSlop; } if (mIsDragging) { mlistener.onDrag(dx,dy); mLastTouchX = x; mLastTouchY = y; if (null != mVeLocityTracker) { mVeLocityTracker.addMovement(ev); } } break; } case MotionEvent.ACTION_CANCEL: { // Recycle VeLocity Tracker if (null != mVeLocityTracker) { mVeLocityTracker.recycle(); mVeLocityTracker = null; } break; } case MotionEvent.ACTION_UP: { if (mIsDragging) { if (null != mVeLocityTracker) { mLastTouchX = getActiveX(ev); mLastTouchY = getActiveY(ev); // Compute veLocity within the last 1000ms mVeLocityTracker.addMovement(ev); mVeLocityTracker.computeCurrentVeLocity(1000); final float vX = mVeLocityTracker.getXVeLocity(),vY = mVeLocityTracker.getYVeLocity(); // If the veLocity is greater than minVeLocity,call // listener if (Math.max(Math.abs(vX),Math.abs(vY)) >= mMinimumVeLocity) { mlistener.onFling(mLastTouchX,mLastTouchY,-vX,-vY); } } } // Recycle VeLocity Tracker if (null != mVeLocityTracker) { mVeLocityTracker.recycle(); mVeLocityTracker = null; } break; } } return result; } } @TargetApi(5) private static class EclairDetector extends CupcakeDetector { private static final int INVALID_POINTER_ID = -1; privatE int mActivePointerId = INVALID_POINTER_ID; privatE int mActivePointerIndex = 0; public EclairDetector(Context context) { super(context); } @Override float getActiveX(MotionEvent ev) { try { return ev.getX(mActivePointerIndeX); } catch (Exception E) { return ev.getX(); } } @Override float getActiveY(MotionEvent ev) { try { return ev.getY(mActivePointerIndeX); } catch (Exception E) { return ev.getY(); } } @Override public Boolean onTouchEvent(MotionEvent ev) { final int action = ev.getAction(); switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: mActivePointerId = ev.getPointerId(0); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: mActivePointerId = INVALID_POINTER_ID; break; case MotionEvent.ACTION_POINTER_UP: final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; final int pointerId = ev.getPointerId(pointerIndeX); if (pointerId == mActivePointerId) { // This was our active pointer going up. Choose a new // active pointer and adjust accordingly. final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mActivePointerId = ev.getPointerId(newPointerIndeX); mLastTouchX = ev.getX(newPointerIndeX); mLastTouchY = ev.getY(newPointerIndeX); } break; } mActivePointerIndex = ev.findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId : 0); return super.onTouchEvent(ev); } } @TargetApi(8) private static class FroyoDetector extends EclairDetector { private final ScaleGestureDetector mDetector; // Needs to be an inner class so that we don't hit // VerifyError's on API 4. private final OnScaleGestureListener mScaleListener = new OnScaleGestureListener() { @Override public Boolean onScale(ScaleGestureDetector detector) { mlistener.onScale(detector.getScaleFactor(),detector.getFocusX(),detector.getFocusY()); return true; } @Override public Boolean onScaleBegin(ScaleGestureDetector detector) { return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { // NO-OP } }; public FroyoDetector(Context context) { super(context); mDetector = new ScaleGestureDetector(context,mScaleListener); } @Override public Boolean isScaling() { return mDetector.isInProgress(); } @Override public Boolean onTouchEvent(MotionEvent ev) { mDetector.onTouchEvent(ev); return super.onTouchEvent(ev); } } }@H_502_24@
10.MainActivity
package com.suo.myimage; import android.os.bundle; import android.app.Activity; import android.view.Menu; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceStatE) { super.onCreate(savedInstanceStatE); setContentView(R.layout.activity_main); } @Override public Boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_main,menu); return true; } }@H_502_24@
activity_main.xml
<RelativeLayout xmlns:android="http://scheR_138_11845@as.android.com/apk/res/android" xmlns:tools="http://scheR_138_11845@as.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:text="@String/Hello_world" /> <com.suo.image.ScaleView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/a"/> </RelativeLayout>@H_502_24@
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
以上是大佬教程为你收集整理的Android 自定义imageview实现图片缩放实例详解全部内容,希望文章能够帮你解决Android 自定义imageview实现图片缩放实例详解所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。