欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 自定义imageview实现图片缩放实例详解

程序员文章站 2022-08-02 14:28:55
android 自定义imageview实现图片缩放实例详解  觉得这个自定义的imageview很好用 性能不错  所以拿出来分享给大家 ...

android 自定义imageview实现图片缩放实例详解

 觉得这个自定义的imageview很好用 性能不错  所以拿出来分享给大家  因为不会做gif图  所以项目效果 就不好贴出来了  把代码贴出来

1.项目结构图

Android 自定义imageview实现图片缩放实例详解

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

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

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

5.scaleview

/******************************************************************************* 
 * 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 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, attr, 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 focalx, float focaly) { 
  mattacher.zoomto(scale, focalx, focaly); 
 } 
 
 @override 
 protected void ondetachedfromwindow() { 
  mattacher.cleanup(); 
  super.ondetachedfromwindow(); 
 } 
 
} 

6.scaleviewattacher  这个是最关键的

/******************************************************************************* 
 * 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.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, 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)) { 
      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, scalefactor, focusx, 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, 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 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 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(); 
 
  /** 
   * 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, 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); 
     break; 
 
    case fit_start: 
     mbasematrix.setrecttorect(mtempsrc, mtempdst, scaletofit.start); 
     break; 
 
    case fit_end: 
     mbasematrix.setrecttorect(mtempsrc, mtempdst, scaletofit.end); 
     break; 
 
    case fit_xy: 
     mbasematrix.setrecttorect(mtempsrc, mtempdst, 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 x, 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, mfocalx, 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, maxx, miny, maxy, 0, 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); 
   } 
  } 
 } 
} 

7.scrollerproxy

/******************************************************************************* 
 * 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.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 velocityx, 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 starty, int velocityx, int velocityy, int minx, int maxx, int miny, int maxy, 
    int overx, int overy) { 
   mscroller.fling(startx, starty, velocityx, velocityy, minx, maxx, miny, maxy, 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, int starty, int velocityx, int velocityy, int minx, int maxx, int miny, int maxy, 
    int overx, int overy) { 
   mscroller.fling(startx, starty, velocityx, velocityy, minx, maxx, miny, maxy); 
  } 
 
  @override 
  public void forcefinished(boolean finished) { 
   mscroller.forcefinished(finished); 
  } 
 
  @override 
  public int getcurrx() { 
   return mscroller.getcurrx(); 
  } 
 
  @override 
  public int getcurry() { 
   return mscroller.getcurry(); 
  } 
 } 
} 

8.sdk16

/******************************************************************************* 
 * 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.annotation.targetapi; 
import android.view.view; 
 
@targetapi(16) 
public class sdk16 { 
 
 public static void postonanimation(view view, runnable r) { 
  view.postonanimation(r); 
 } 
  
} 

9.versionedgesturedetector

package com.suo.image; 
 
/******************************************************************************* 
 * 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. 
 *******************************************************************************/ 
 
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 starty, float velocityx, float velocityy); 
 
  public void onscale(float scalefactor, float focusx, 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); 
  } 
 
 } 
} 

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

activity_main.xml

<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" 
 xmlns:tools="http://schemas.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> 

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!