18

I want to be able to rotate, zoom and move an image in android framelayout and imageview. With the following code I am able to do it. But the view sometimes gets out of UI. As you can c I handle the rotation in ACTION_UP(90 degree rotation for each ACTION_UP) motionevent. following is my main.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" >
   <ImageView android:id="@+id/imageView"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
         android:src="@drawable/csf6a4_flipped"
         android:scaleType="matrix" >
   </ImageView>
</FrameLayout>

and following is my java file:

package com.example.rotatezoommove;



import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.os.Bundle;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;

public class RotateZoomMove extends Activity implements OnTouchListener {
   private static final String TAG = "Touch";
   // These matrices will be used to move and zoom image
   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;

   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
      ImageView view = (ImageView) findViewById(R.id.imageView);
      view.setOnTouchListener(this);
   }

   @Override
   public boolean onTouch(View v, MotionEvent event) {
      ImageView view = (ImageView) v;
      int rotation = 25;
      // Dump touch event to log
      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:

         mode = NONE;
         Log.d(TAG, "mode=NONE");
         savedMatrix.set(matrix);
         matrix.postRotate(90);

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

      view.setImageMatrix(matrix);
      return true; // indicate event was handled
   }



/** Show an event in the LogCat view, for debugging */
   private void dumpEvent(MotionEvent 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(MotionEvent 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, MotionEvent event) {
      float x = event.getX(0) + event.getX(1);
      float y = event.getY(0) + event.getY(1);
      point.set(x / 2, y / 2);
   }
}

I guess I'm almost there. Can anyone suggest any solutions so that I can rotate, drag and zoom the image and keep the image within UI all the time?

Sathyajith Bhat
  • 21,321
  • 22
  • 95
  • 134
kzs
  • 1,111
  • 5
  • 20
  • 35
  • Without never trying multitouch I'm wondering do you have two+ pointers when calling spacing and midPoint -methods? Or did I just fail to see such check. Or maybe it's irrelevant. – harism May 05 '11 at 08:57
  • Can you please post full source code as I am trying since 2 days and unable to achieve this. Thanks in advance. – Nikhil Oct 19 '11 at 13:55
  • Hi Nik..the source code is already given in the post and it's not mine. Try combining this code with the solution given below in the answer. – kzs Oct 21 '11 at 04:36
  • @Sathya have find any solution in this..what is problem because i got same thing. – J_K May 17 '13 at 12:50
  • @J_K I didn't ask the question./ – Sathyajith Bhat May 17 '13 at 13:02
  • @Sathya ya but have find any solution about this.. – J_K May 17 '13 at 13:03
  • The image is leaving the view because you are rotating the imageView which does not have uniform dimensions. Make sure your ImageView has both the width and height dimensions equal for a uniform rotation – Daniel Nyamasyo Dec 16 '16 at 09:19

3 Answers3

19

Firstly, You have to take ImageView in your .xml file.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<ImageView
    android:id="@+id/imageView1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_launcher" />

 </LinearLayout>    

Then, Do intialize your Imageview in Java and set onTouchListener to your ImageView.

ImageView imageView=(ImageView) findViewById(R.id.imageView1);
imageView.setOnTouchListener(this);

Implement onTouchListener to your Activity and add unimplemented method to Activity. Now, Finally write this code in your onTouch.

@Override
public boolean onTouch(View v, MotionEvent event) {

       ImageView view = (ImageView) v;
       view.setScaleType(ImageView.ScaleType.MATRIX);
       float scale;      

       // Dump touch event to log
       dumpEvent(event);

       // Handle touch events here...
       switch (event.getAction() & MotionEvent.ACTION_MASK) {
       case MotionEvent.ACTION_DOWN: //first finger down only
          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);
        if (oldDist > 10f) {
            savedMatrix.set(matrix);
            midPoint(mid, event);
            mode = ZOOM;
        }
        lastEvent = new float[4];
        lastEvent[0] = event.getX(0);
        lastEvent[1] = event.getX(1);
        lastEvent[2] = event.getY(0);
        lastEvent[3] = event.getY(1);
        d = rotation(event);
        break;

       case MotionEvent.ACTION_UP: //first finger lifted
       case MotionEvent.ACTION_POINTER_UP: //second finger lifted
          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 && event.getPointerCount() == 2) {
            float newDist = spacing(event);
            matrix.set(savedMatrix);
            if (newDist > 10f) {
                scale = newDist / oldDist;
                matrix.postScale(scale, scale, mid.x, mid.y);
            }
            if (lastEvent != null) {
                newRot = rotation(event);
                float r = newRot - d;
                matrix.postRotate(r, view.getMeasuredWidth() / 2,
                        view.getMeasuredHeight() / 2);
            }
        }
        break;

       }         
       // Perform the transformation
       view.setImageMatrix(matrix); 

       return true; // indicate event was handled

    }
        private float rotation(MotionEvent event) {
        double delta_x = (event.getX(0) - event.getX(1));
        double delta_y = (event.getY(0) - event.getY(1));
        double radians = Math.atan2(delta_y, delta_x);

        return (float) Math.toDegrees(radians);
    }

    private float spacing(MotionEvent event) {
       float x = event.getX(0) - event.getX(1);
       float y = event.getY(0) - event.getY(1);
       return FloatMath.sqrt(x * x + y * y);

    }

    private void midPoint(PointF point, MotionEvent event) {
       float x = event.getX(0) + event.getX(1);
       float y = event.getY(0) + event.getY(1);
       point.set(x/2, y/2);

    }


    /** Show an event in the LogCat view, for debugging */

    private void dumpEvent(MotionEvent 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());

    }   
Chintan
  • 251
  • 2
  • 6
  • Hi ,thanx for grate solution but i want to add multiple image inside one layout and all images sense Touch event,Can you help how to do so? – Pranav Oct 02 '15 at 13:05
  • I've tried this and it really works! I just want to know if the rotation of the image can be smoother? Sometimes it rotates as if the image is bigger than it really is? – jlively May 23 '17 at 18:09
  • Too many errors in 2023. – Kishan Solanki Mar 17 '23 at 14:13
6

call this method from inside from:

setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {

..........................................................

fixing();
setImageMatrix(savedMatrix2);

before the end of return

public void fixing()
{

float[] value = new float[9];
        matrix.getValues(value);

float[] savedValue = new float[9];
        savedMatrix2.getValues(savedValue);

int width = getWidth();
        int height = getHeight();

        Drawable d = getDrawable();
        if (d == null)  return;
        int imageWidth = d.getIntrinsicWidth();
        int imageHeight = d.getIntrinsicHeight();
        int scaleWidth = (int) (imageWidth * value[0]);
        int scaleHeight = (int) (imageHeight * value[4]);

// don't let the image go outside
        if (value[2] > width-1)   
            value[2] = width-10;
        else if (value[5] > height - 1)   
            value[5] = height - 10;
        else if (value[2] < -(scaleWidth-1))  
            value[2] = -(scaleWidth-10);
        else if (value[5] < -(scaleHeight-1))   
            value[5] = -(scaleHeight-10);

        // maximum zoom ratio: MAx
        if (value[0] > MAX_ZOOM || value[4] > MAX_ZOOM){
            value[0] = MAX_ZOOM;
            value[4] = MAX_ZOOM;
            //value[2] = savedValue[2];
            //value[5] = savedValue[5];
        }

        matrix.setValues(value); 
        savedMatrix2.set(matrix);
}
Sathyajith Bhat
  • 21,321
  • 22
  • 95
  • 134
5

It's easy to move, scale and rotate sticker using Android StickerView

check this tutorial for step by step guide: click here

enter image description here

nirav kalola
  • 1,220
  • 13
  • 17