See Updated Tutorial
Here we use Bitmap, Gesture, Matrix and other Bitmap Basic function. I have develop simply three classes.
main class is TouchImageView.java that is a Image-View.you can set this class Object anywhere
Make one project and use my classes
main class TouchImageView.java
package com.ahmad;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
public class TouchImageView extends ImageView
{
private static final String TAG = "Touch";
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// Remember some things for zooming
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
Context context;
public TouchImageView(Context context)
{
super(context);
super.setClickable(true);
this.context = context;
matrix.setTranslate(1f, 1f);
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
setOnTouchListener(new OnTouchListener()
{
@Override
public boolean onTouch(View v, MotionEvent rawEvent)
{
WrapMotionEvent event = WrapMotionEvent.wrap(rawEvent);
// Dump touch event to log
// if (Viewer.isDebug == true)
{
// dumpEvent(event);
//
}
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d(TAG, "mode=DRAG");
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
Log.d(TAG, "oldDist=" + oldDist);
if (oldDist > 10f)
{
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
Log.d(TAG, "mode=ZOOM");
}
break;
case MotionEvent.ACTION_UP:
int xDiff = (int) Math.abs(event.getX() - start.x);
int yDiff = (int) Math.abs(event.getY() - start.y);
if (xDiff < 8 && yDiff < 8)
{
performClick();
}
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
Log.d(TAG, "mode=NONE");
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG)
{
// ...
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x, event.getY() - start.y);
}
else if (mode == ZOOM)
{
float newDist = spacing(event);
Log.d(TAG, "newDist=" + newDist);
if (newDist > 10f)
{
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
}
setImageMatrix(matrix);
return true; // indicate event was handled
}
}
);
}
public void setImage(Bitmap bm, int displayWidth, int displayHeight)
{
super.setImageBitmap(bm);
//Fit to screen.
float scale;
if ((displayHeight / bm.getHeight()) >= (displayWidth / bm.getWidth()))
{
scale = (float)displayWidth / (float)bm.getWidth();
}
else
{
scale = (float)displayHeight / (float)bm.getHeight();
}
savedMatrix.set(matrix);
matrix.set(savedMatrix);
matrix.postScale(scale, scale, mid.x, mid.y);
setImageMatrix(matrix);
// Center the image
float redundantYSpace = (float)displayHeight - (scale * (float)bm.getHeight()) ;
float redundantXSpace = (float)displayWidth - (scale * (float)bm.getWidth());
redundantYSpace /= (float)2;
redundantXSpace /= (float)2;
savedMatrix.set(matrix);
matrix.set(savedMatrix);
matrix.postTranslate(redundantXSpace, redundantYSpace);
setImageMatrix(matrix);
}
/** Show an event in the LogCat view, for debugging */
@SuppressWarnings("unused")
private void dumpEvent(WrapMotionEvent event)
{
String names[] =
{
"DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE",
"POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?"
}
;
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_").append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN
|| actionCode == MotionEvent.ACTION_POINTER_UP)
{
sb.append("(pid ").append(
action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
sb.append(")");
}
sb.append("[");
for (int i = 0; i < event.getPointerCount(); i++)
{
sb.append("#").append(i);
sb.append("(pid ").append(event.getPointerId(i));
sb.append(")=").append((int) event.getX(i));
sb.append(",").append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";");
}
sb.append("]");
Log.d(TAG, sb.toString());
}
/** Determine the space between the first two fingers */
private float spacing(WrapMotionEvent event)
{
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
/** Calculate the mid point of the first two fingers */
private void midPoint(PointF point, WrapMotionEvent event)
{
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
}
Motion class to help in touch event
package com.ahmad;
import android.view.MotionEvent;
public class EclairMotionEvent extends WrapMotionEvent {
protected EclairMotionEvent(MotionEvent event) {
super(event);
}
public float getX(int pointerIndex) {
return event.getX(pointerIndex);
}
public float getY(int pointerIndex) {
return event.getY(pointerIndex);
}
public int getPointerCount() {
return event.getPointerCount();
}
public int getPointerId(int pointerIndex) {
return event.getPointerId(pointerIndex);
}
}
Class to keep information about Pointer ID
package com.ahmad;
import android.view.MotionEvent;
public class WrapMotionEvent {
protected MotionEvent event;
protected WrapMotionEvent(MotionEvent event) {
this.event = event;
}
static public WrapMotionEvent wrap(MotionEvent event) {
try {
return new EclairMotionEvent(event);
} catch (VerifyError e) {
return new WrapMotionEvent(event);
}
}
public int getAction() {
return event.getAction();
}
public float getX() {
return event.getX();
}
public float getX(int pointerIndex) {
verifyPointerIndex(pointerIndex);
return getX();
}
public float getY() {
return event.getY();
}
public float getY(int pointerIndex) {
verifyPointerIndex(pointerIndex);
return getY();
}
public int getPointerCount() {
return 1;
}
public int getPointerId(int pointerIndex) {
verifyPointerIndex(pointerIndex);
return 0;
}
private void verifyPointerIndex(int pointerIndex) {
if (pointerIndex > 0) {
throw new IllegalArgumentException(
"Invalid pointer index for Donut/Cupcake");
}
}
}
Notable thing is that this pinch zoom will only work above Android 2.0. Lower version does not support multiple finger
Nice dear
ReplyDeleteThanks....
DeleteThis comment has been removed by the author.
ReplyDeleteHow can you implement the pan/pinch zoom limits ?
ReplyDeleteInside touch's Action_Move i have a code to zoom or drag.If you touch with one finger then it move image to show complete image else it will zoom.Here you can do two thing two to limit zoom level check image size (using matrix).On second way i am working so i will update as soon as i do
DeleteThanks for such a great solution. I've been working on this, so I
Deleteimplemented limits for zooming and moving a picture. Hope it helps :)!
ZOOMING LIMITS
//You need this first:
double max_zoom=3,min_zoom=0.4; //or put other limits, 3 and 0.4 is example
//Right above the line "case MotionEvent.ACTION_POINTER_UP:" put this code:
float f[] = new float[9];
matrix.getValues(f);
if (f[0]>max_zoom){
matrix.postScale((float)max_zoom/f[0], (float)max_zoom/f[0], mid.x, mid.y);
}
if (f[0]bmp_picture.getWidth()*f[0]-marg_left){
matrix.postTranslate(-(f[2]+bmp_picture.getWidth()*f[0])+marg_left, 0);
}
if (-f[5]>bmp_picture.getHeight()*f[0]-marg_top){
matrix.postTranslate(0,-(f[5]+bmp_picture.getHeight()*f[0])+marg_top);
}
if (f[2]>win_w-marg_right){
matrix.postTranslate(-(f[2]-win_w+marg_right),0);
}
if (f[5]>win_h-marg_botom){
matrix.postTranslate(0,-(f[5]-win_h+marg_botom));
}
Thank you for providing solution..I have not checked it hope it work correct.These days i am not getting time to post alternative solution.By the way thank you
DeleteI see in comment above the code I sent is missing, so I will send you separately code for limits for Zoom and for Move.
DeleteZOOMING LIMITS
//You need this first:
double max_zoom=3,min_zoom=0.4; //or put other limits, 3 and 0.4 is example
//Right above the line "case MotionEvent.ACTION_POINTER_UP:" put this code:
float f[] = new float[9];
matrix.getValues(f);
if (f[0]>max_zoom){
matrix.postScale((float)max_zoom/f[0], (float)max_zoom/f[0], mid.x, mid.y);
}
if (f[0]<min_zoom){
matrix.postScale((float)min_zoom/f[0],(float) min_zoom/f[0], mid.x, mid.y);
}
MOVING LIMITS
Delete//First, you need to put this:
int marg_left=50,marg_right=50,marg_top=50,marg_botom=50,win_w,win_h;
Bitmap bmp_picture;
//Next, you need to put 2 new arguments in set_Image to look just like this:
public void setImage(Bitmap bm, int displayWidth, int displayHeight,int h,int w) {
bmp_picture = bm;
win_h = h;
win_w = w;
// h and w are the screen width and height.
// I calculated them in other class like this:
Display display = getWindowManager().getDefaultDisplay();
h=display.getHeight()
w=display.getWidth()
//Finaly, right above the line "case MotionEvent.ACTION_POINTER_UP:" put this:
if (-f[2]>bmp_picture.getWidth()*f[0]-marg_left){
matrix.postTranslate(-(f[2]+bmp_picture.getWidth()*f[0])+marg_left, 0);
}
if (-f[5]>bmp_picture.getHeight()*f[0]-marg_top){
matrix.postTranslate(0,-(f[5]+bmp_picture.getHeight()*f[0])+marg_top);
}
if (f[2]>win_w-marg_right){
matrix.postTranslate(-(f[2]-win_w+marg_right),0);
}
if (f[5]>win_h-marg_botom){
matrix.postTranslate(0,-(f[5]-win_h+marg_botom));
}
And no problem, glad if I can make some help to you, as you did to me ;)!
DeleteHi Tofeeq,
ReplyDeleteI'm a newbie. I' appreciate some help. Your code is compiling perfectly well.
I've got all three classes in my project.
But I'm not sure on how to use it. Might sound stupid, but thats how it is.
Could you give me a push in the right direction?
Thanks,
Sid
Main Class is TouchImageView. This extends ImageView and will work as ImageView.Create Object of TouchImageView and add to your desired layout in your activity.
DeleteCool. Works Well for images. How about layouts ?
ReplyDeleteI want to apply pinch-zoom functionality for a "Frame Layout".
setImageMatrix(matrix);
//This is only for images, any thing similar we have for layouts?
I had not did it yet ..but you have to create custom classes for achieving it.Its going to be tough.You should start from TouchImageView.Extends Frame Layout instead of Image View.
DeleteThanks bro
DeleteHello,
ReplyDeleteCan you share this codes in project files? Because I got errors when i had tried to build this project. Sorry for my english.
Thank you.
whenever i got time i will share code..:)
Deletehttps://github.com/MikeOrtiz/TouchImageView.git
ReplyDeleteHow to use the TouchImageView in an activity??
ReplyDeletecan u please share the TouchImageView class with the zoom and move limit?
ReplyDeletei love ya man.. i have been behind this shit for the past 1 month!! u rock!!!!!
ReplyDeletehw did u use touchimageview in your activity??
Deletei have struck in dis for a week...
pls help
Very nice tutorial, however is there a way to implement this to a webview?
ReplyDeletehi, Can anyone post a link with the TouchImageView class with zoom and move limit?
ReplyDeletePlease
/R
nice work man,keep it up!
ReplyDeleteThank you hazaribagh
DeleteHow to use the TouchImageView in an activity??
ReplyDeletepls help its very urgent for me
Sorry for late reply. You can use TouchImageView like ImageView. if you want to use in xml then use like
ReplyDeleteand then TouchImage touc=findViewByID(R.id.itsid).
is it possible to zoom two imageviews in this??
DeleteYes, you can two image view also.I am not explain it here more. Try it and let me know if it works
DeleteTouchImageView view = (TouchImageView ) findViewById(R.id.picture);
Deleteview.setImageResource(R.drawable.neck1);
i have put the above code i my activity....but i m getting error that failed the create activity..
pls help its very urgent for me
Failed the create activity !!!!..explain your Error properly so that i can help you
DeleteHi there,
ReplyDeleteI'm a newbie trying to get this feature to work. I have managed to compile the above code without issue, but the software crashes when I try opening a TouchImageView.
What should the contents of the layout XML be?
I'm using the following code to call the activity...
...
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TouchImageView view = (TouchImageView ) findViewById(R.id.name_of_layout_XML);
view.setImageResource(R.drawable.drawable_name);
}
...
My layout XML has the following;
...
<?xml version="1.0" encoding="utf-8"?>
<TouchImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_name"
android:id="@+id/name_of _layout_XML"
android:background="@drawable/name_of_drawable">
</TouchImageView>
...
Any help would be much appreciated :-)
Append complete package name in xml
Deletehelp this one...
ReplyDeleteThank you for your code, but I had errors, could you please share code.
ReplyDeleteSuleyman
See GitHub URL in starting of tutorial.
DeleteI had problem with this kind of symbols: & ;
ReplyDeletelike: if (oldDist > 10f)
I did not get. Please be more specific where you got this issue
DeleteHi, you are posting un related link here. That's why i mark them spam. Please take care of this.
ReplyDelete