java - How does pinch zoom work with panning for image in Android -



goal

an activity made view image, can pinch zoom or pan image. image centered in screen in beginning. pinch zoom centered @ center of image, after image panned somewhere else in screen.

the image displaying downloaded given url, , url passed of intent start image viewing activity.

pinch zoom implemented postscale(), pan posttranslate().


problem

after panning image somewhere, pinch-zoom center still @ center of screen. tried follow center of image when it's been moved new place code doesn't work way. please give idea.

the image downloading , panning work well.


code

activity_image_viewer_layout.xml

<?xml version="1.0" encoding="utf-8"?> <framelayout     xmlns:android="http://schemas.android.com/apk/res/android"     android:layout_width="fill_parent"     android:layout_height="fill_parent" >      <linearlayout         android:orientation="vertical"          android:layout_width="fill_parent"         android:layout_height="fill_parent"         android:gravity="center"         android:background="@color/mypureblack" >          <linearlayout           android:id="@+id/progressbar_wrapper"             android:layout_width="fill_parent"             android:layout_height="wrap_content"             android:orientation="vertical" >              <progressbar                  android:id="@+id/progressbar"                 style="?android:attr/progressbarstylehorizontal"                  android:layout_width="fill_parent"                 android:layout_height="wrap_content"                 android:max="100"                 android:progress="0"                 android:layout_marginleft="20dp"                 android:layout_marginright="20dp"                 android:layout_gravity="center" >             </progressbar>         </linearlayout>          <imageview             android:id="@+id/image_viewer"              android:visibility="gone"             android:layout_width="fill_parent"             android:layout_height="fill_parent"              android:background="@color/mypureblack"             android:scaletype="matrix" >         </imageview>     </linearlayout> </framelayout> 

activityimageviewer.java

package com.com2us.hubapp.android;  import java.io.bufferedinputstream; import java.io.file; import java.io.fileoutputstream; import java.io.ioexception; import java.io.inputstream; import java.io.outputstream; import java.lang.reflect.invocationtargetexception; import java.lang.reflect.method; import java.net.url; import java.net.urlconnection;  import org.apache.http.util.bytearraybuffer;  import android.app.activity; import android.content.res.configuration; import android.graphics.bitmap; import android.graphics.bitmapfactory; import android.graphics.matrix; import android.graphics.pointf; import android.graphics.rect; import android.graphics.rectf; import android.graphics.drawable.bitmapdrawable; import android.graphics.drawable.drawable; import android.net.uri; import android.os.asynctask; import android.os.bundle; import android.util.floatmath; import android.util.log; import android.view.keyevent; import android.view.motionevent; import android.view.view; import android.view.view.ontouchlistener; import android.view.viewtreeobserver; import android.view.viewtreeobserver.ongloballayoutlistener; import android.view.animation.alphaanimation; import android.widget.imageview; import android.widget.linearlayout; import android.widget.progressbar;  public class activityimageviewer extends activity {     file imagefile = null;      // matrices pinch zoom , pan     matrix matrix = new matrix();     matrix savedmatrix = new matrix();     matrix savedmatrixzoom = new matrix();      // state of motion event     static final int none = 0;     static final int pan = 1;     static final int pinch_zoom = 2;     int mode = none;      // first pointer down     pointf start = new pointf();      // center of image (failed track when image has been moved)     pointf centerofimage = new pointf();      // oldest cartesian distance between first 2 pointers when second pointer down     float olddist = 1f;      // min_scale/max_scale min/max scale factor     private final float min_scale = 0.5f;     private final float max_scale = 3.0f;      // touch_sensitive minimum cartesian distance between first 2 pointers triggers pinch zoom     private final float touch_sensitive = 10.0f;     private final float spacing_left_and_right = 30.0f;     private final float spacing_top_and_bottom = 30.0f;      // imageview widget     private imageview image_viewer;      // progress bar shows current progress before image downloading completed     private progressbar progressbar;     private linearlayout progressbar_wrapper;      // async task downloads image given url     private downloadfilestask downloadfilestask;      private class downloadfilestask extends asynctask<string, integer, bitmap> {         protected bitmap doinbackground(string... urls) {             inputstream input = null;             outputstream output = null;             try {                 url url = new url(urls[0]);                 urlconnection connection = url.openconnection();                 connection.connect();                 int lenghtoffile = connection.getcontentlength();                 // download file                 inputstream = connection.getinputstream();                 bufferedinputstream bis = new bufferedinputstream(is, 8190);                  bytearraybuffer baf = new bytearraybuffer(50);                 int current = 0;                 while ((current = bis.read()) != -1) {                     baf.append((byte)current);                 }                 byte[] imagedata = baf.tobytearray();                 bitmap bmp = bitmapfactory.decodebytearray(imagedata, 0, imagedata.length);                  //final int percent = (int) (total * 100 / lenghtoffile);                 //publishprogress(percent);                 //lenghtoffile                  return bmp;             } catch (exception e) {              } {                 try {                     if (output != null)                         output.close();                     output = null;                 } catch (ioexception e) {                  }                 try {                     if (input != null)                         input.close();                     input = null;                 } catch (ioexception e) {                  }              }             return null;         } // protected bitmap doinbackground(string... urls) {}          protected void onprogressupdate(integer... progress) {             progressbar.setprogress(progress[0]);         }          protected void onpostexecute(bitmap bmp) {             if (bmp != null) {                 final alphaanimation animationafter = new alphaanimation(0.0f, 1.0f);                 animationafter.setduration(300);                 animationafter.setfillenabled(true);                 animationafter.setfillafter(true);                 image_viewer.setanimation(animationafter);                 image_viewer.setimagebitmap(bmp);                 viewtreeobserver viewtreeobserver = image_viewer.getviewtreeobserver();                 viewtreeobserver.addongloballayoutlistener(new ongloballayoutlistener() {                     @override                     public void ongloballayout() {                         drawable drawable = image_viewer.getdrawable();                         int dx = (image_viewer.getwidth() - drawable.getintrinsicwidth()) / 2;                         int dy = (image_viewer.getheight() - drawable.getintrinsicheight()) / 2;                         matrix.posttranslate(dx, dy);                         image_viewer.setimagematrix(matrix);                     }                 });                 progressbar_wrapper.setvisibility(view.gone);                 image_viewer.setvisibility(view.visible);             } else {                 android.os.handler handler = new android.os.handler();                 handler.postdelayed(new runnable() {                     @override                     public void run() {                         finish();                     }                 }, 2000);             }         } // end of protected void onpostexecute(bitmap bmp) {}     } // end of private class downloadfilestask extends asynctask<string, integer, bitmap> {}      // these activity life cycle handling     // oncreate     @override     public void oncreate(bundle savedinstancestate) {         //settheme(r.style.hubtheme);         super.oncreate(savedinstancestate);         setcontentview(r.layout.activity_image_viewer);         progressbar_wrapper = (linearlayout) findviewbyid(r.id.progressbar_wrapper);         image_viewer = (imageview) findviewbyid(r.id.image_viewer);         progressbar = (progressbar) findviewbyid(r.id.progressbar);         image_viewer.setontouchlistener(new myontouchlistener());         final string uriforimage = getintent().getstringextra("url");         downloadfilestask = new downloadfilestask();         downloadfilestask.execute(uriforimage);     }      // onstart     @override     protected void onstart() {         super.onstart();     }      // onresume     @override     protected void onresume() {         super.onresume();     }      // onpause     @override     protected void onpause() {         super.onpause();     }      // onstop     @override     protected void onstop() {         super.onstop();     }      // onrestart     @override     protected void onrestart() {         super.onrestart();     }      // ondestroy     @override     protected void ondestroy() {         super.ondestroy();         if (imagefile != null) {             try {                 drawable drawable = image_viewer.getdrawable();                 bitmapdrawable bitmapdrawable = (bitmapdrawable) drawable;                 bitmap bitmap = bitmapdrawable.getbitmap();                 bitmap.recycle();                  drawable = null;                 bitmapdrawable = null;                 bitmap = null;              } catch (nullpointerexception e) {             }         }     }      // onkeydown     @override     public boolean onkeydown(int keycode, keyevent event) {         if (keycode == keyevent.keycode_back) {             this.onbackpressed();         }         return true;     }      // onbackpressed     public void onbackpressed() {         finish();     }      // onconfigurationchanged     @override     public void onconfigurationchanged(configuration newconfig) {          super.onconfigurationchanged(newconfig);         if (newconfig.equals(configuration.orientation_landscape)) {          } else if (newconfig.equals(configuration.orientation_portrait)) {          }     }      // onlowmemory     @override     public void onlowmemory() {         super.onlowmemory();         finish();     }      // cartesian distance between first 2 pointers     private float spacing(motionevent event) {         float x = 0;         float y = 0;         try {             method getx = motionevent.class.getmethod("getx", integer.type);             method gety = motionevent.class.getmethod("getx", integer.type);              // x = event.getx(0) - event.getx(1);             // y = event.gety(0) - event.gety(1);             float x1 = (float) getx.invoke(event, 0);             float x2 = (float) getx.invoke(event, 1);             x = x1 - x2;             float y1 = (float) gety.invoke(event, 0);             float y2 = (float) gety.invoke(event, 1);             y = y1 - y2;          } catch (securityexception e) {         } catch (nosuchmethodexception e) {         } catch (illegalargumentexception e) {         } catch (illegalaccessexception e) {         } catch (invocationtargetexception e) {         }         return floatmath.sqrt(x * x + y * y);     }      // flags set manually convenience     private final int motionevent_action_mask         = 255; // 0xff or 11111111     private final int motionevent_action_pointer_down = 5;   // 101     private final int motionevent_action_pointer_up   = 6;   // 110      private class myontouchlistener implements ontouchlistener {          // ontouch         @override         public boolean ontouch(view v, motionevent event) {             imageview view = (imageview) v;             drawable drawable = view.getdrawable();             if (drawable == null)                 return true;             switch (event.getaction() & motionevent_action_mask) {             case motionevent.action_down:                 savedmatrix.set(matrix);                 start.set(event.getx(), event.gety());                 mode = pan;                 break;             case motionevent_action_pointer_down:                 olddist = spacing(event);                 if (olddist > touch_sensitive) {                     savedmatrix.set(matrix);                     mode = pinch_zoom;                 }                 break;             case motionevent.action_up:             case motionevent_action_pointer_up:                 mode = none;                 break;             case motionevent.action_move:                 if (mode == pan) {                     // /////////////////////////////////////////                     matrix.set(savedmatrix);                     float[] matrixvalues = new float[9];                     rect viewrect = new rect(view.getleft(), view.gettop(), view.getright(), view.getbottom());                     matrix.getvalues(matrixvalues);                     float currenty = matrixvalues[matrix.mtrans_y];                     float currentx = matrixvalues[matrix.mtrans_x];                     float currentscale = matrixvalues[matrix.mscale_x];                     float currentheight = drawable.getintrinsicheight() * currentscale;                     float currentwidth = drawable.getintrinsicwidth() * currentscale;                     float dx = event.getx() - start.x;                     float dy = event.gety() - start.y;                     float newx = currentx + dx;                     float newy = currenty + dy;                      rectf drawingrect = new rectf(newx, newy, newx + currentwidth, newy + currentheight);                     float diffup = math.min(viewrect.bottom - drawingrect.bottom, viewrect.top - drawingrect.top) - spacing_top_and_bottom;                     float diffdown = math.max(viewrect.bottom - drawingrect.bottom, viewrect.top - drawingrect.top) + spacing_top_and_bottom;                     float diffleft = math.min(viewrect.left - drawingrect.left, viewrect.right - drawingrect.right) - spacing_left_and_right;                     float diffright = math.max(viewrect.left - drawingrect.left, viewrect.right - drawingrect.right) + spacing_left_and_right;                     if (diffup > 0) {                         dy += diffup;                     }                     if (diffdown < 0) {                         dy += diffdown;                     }                     if (diffleft > 0) {                         dx += diffleft;                     }                     if (diffright < 0) {                         dx += diffright;                     }                     matrix.posttranslate(dx, dy);                 } else if (mode == pinch_zoom) {                     float newdist = spacing(event);                     if (newdist > touch_sensitive) {                         matrix.set(savedmatrix);                         float scale = newdist / olddist;                          // center of image. (failed when image has been moved)                         rect viewrect = new rect(view.getleft(), view.gettop(), view.getright(), view.getbottom());                         centerofimage.x = viewrect.centerx();                         centerofimage.y = viewrect.centery();                          float[] f = new float[9];                         matrix tmp = new matrix(matrix);                         tmp.postscale(scale, scale, centerofimage.x, centerofimage.y);                         tmp.getvalues(f);                         float scalex = f[matrix.mscale_x];                         if (scalex < min_scale || scalex > max_scale) {                             matrix.set(savedmatrixzoom);                         } else {                             matrix.postscale(scale, scale, centerofimage.x, centerofimage.y);                             savedmatrixzoom.set(matrix);                         }                       }                 }                 break;             }             view.setimagematrix(matrix);             return true;         } // end of public boolean ontouch(view v, motionevent event) {}      } // end of private class myontouchlistener implements ontouchlistener {}  } // end of public class activityimageviewer extends activity {} 

you can use scale gesture detector pinch zoom. instead of creating pinch zoom scratch can following,

public class mycustomview extends view {  private scalegesturedetector mscaledetector; private float mscalefactor = 1.f;  public mycustomview(context mcontext){     ...     // view code goes here     ...     mscaledetector = new scalegesturedetector(context, new scalelistener()); }  @override public boolean ontouchevent(motionevent ev) {     // let scalegesturedetector inspect events.     mscaledetector.ontouchevent(ev);     return true; }  @override public void ondraw(canvas canvas) {     super.ondraw(canvas);      canvas.save();     canvas.scale(mscalefactor, mscalefactor);     ...     // ondraw() code goes here     ...     canvas.restore(); }  private class scalelistener          extends scalegesturedetector.simpleonscalegesturelistener {     @override     public boolean onscale(scalegesturedetector detector) {         mscalefactor *= detector.getscalefactor();          // don't let object small or large.         mscalefactor = math.max(0.1f, math.min(mscalefactor, 5.0f));          invalidate();         return true;     } } } 

note : translation reside in ondraw method scale image.


Comments

Popular posts from this blog

c# - Send Image in Json : 400 Bad request -

javascript - addthis share facebook and google+ url -

ios - Show keyboard with UITextField in the input accessory view -