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
Post a Comment