move,scale and crop the image in android like facebook profile picture

18,391

I had a similar requirement. Created a cropping component using modified Photoview and Cropper library. User can select image from Gallery or take a picture from Camera and then crop the image with move and scale, similar to Facebook profile picture cropping. Sharing the project on github.

Please check the github project here: https://github.com/ozeetee/AndroidImageZoomCrop

Share:
18,391
Jagdish
Author by

Jagdish

Working as Sr. Android application developer. Develop more intuitive and highly coding standard application. http://stackoverflow.com/users/1986489/jagdishkumawat

Updated on June 15, 2022

Comments

  • Jagdish
    Jagdish almost 2 years

    I want crop an image in my application.I tried a lot but not succeed.Its challenge to android developer. Is there any idea or reference to implement the features move,scale and crop the image in android like facebook upload profile pic. Now i am able to move, scale and crop and the image.But not consistant like facebook.I want to set fix image scale based on orientation.I attached the screen shot-3.

    My code as below:-

    crop_image.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical" >
    
        <FrameLayout
            android:id="@+id/flCrop"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1" >
    
            <ImageView
                android:id="@+id/img"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:scaleType="matrix"
                android:src="@drawable/nature" />
    
            <ImageView
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:layout_gravity="center"
                android:scaleType="fitXY"
                android:src="@drawable/blur_with_frame"
                android:visibility="visible" />
    
            <ImageView
                android:id="@+id/troll_face"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="5dp"
                android:layout_marginLeft="10dp"
                android:layout_gravity="bottom" />
        </FrameLayout>
    
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="horizontal" >
    
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="btnSelectPic"
                android:layout_marginRight="10dp"
                android:text="select Pic" />
    
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:onClick="btnCrop"
                android:text="Crop" />
        </LinearLayout>
    
    </LinearLayout>
    

    MainActivity.java

    package com.hotveryspicy.maskimage;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Matrix;
    import android.graphics.PointF;
    import android.net.Uri;
    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.view.ViewTreeObserver.OnGlobalLayoutListener;
    import android.widget.FrameLayout;
    import android.widget.ImageView;
    import android.widget.ImageView.ScaleType;
    
    import com.ipaulpro.afilechooser.utils.FileUtils;
    
    public class MainActivity extends Activity implements OnTouchListener{
        ImageView img;
        FrameLayout flCrop;
    
        int framWidth = 0;
        int framHeight = 0;
    
        int imageHeight ;
        int imageWidth ;
    
        int cropImageWidth = 320;
        int cropImageHeight = 263;
    
        public static int SELECT_PHOTO =0;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.crop_image);
            img = (ImageView) findViewById(R.id.img);
            flCrop = (FrameLayout) findViewById(R.id.flCrop);
            img.setOnTouchListener(this);
    
    
        }
    
        public void btnCrop(View v) {
            ImageView troll_face = (ImageView) findViewById(R.id.troll_face);
            makeMaskImage(troll_face, R.drawable.nature);
        }
    
        public void btnSelectPic(View v){
            Intent intent = new Intent(Intent.ACTION_PICK,
                    android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
            startActivityForResult(intent, 0);
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
    
            switch (resultCode) {
            case RESULT_OK:
                Uri targetUri = data.getData();
    
                final String path = FileUtils.getPath(this, targetUri);
    
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
    
                Bitmap bitmap = null;
                try {
    
                    bitmap = BitmapFactory
                            .decodeFile(path);
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                imageHeight = options.outHeight;
                imageWidth = options.outWidth;
    
                img.setOnTouchListener(MainActivity.this);
    
                img.setImageBitmap(bitmap);
    
                break;
            }
        }
    
        // Method of creating mask runtime
        public void makeMaskImage(ImageView mImageView, int mContent) {
    
            flCrop.setDrawingCacheEnabled(true);
            Bitmap bitmap = Bitmap.createBitmap(flCrop.getDrawingCache());
            flCrop.setDrawingCacheEnabled(false);
    
            Log.e("TEST", "Frame W : " + framWidth + " H : " + framHeight);
            Log.e("TEST",
                    "Bitmap W : " + bitmap.getWidth() + " H : "
                            + bitmap.getHeight());
    
            Bitmap result = Bitmap.createBitmap(bitmap, (framWidth / 2)
                    - (cropImageWidth / 2) +1, (framHeight / 2)
                    - (cropImageHeight / 2), cropImageWidth -2, cropImageHeight);
            mImageView.setImageBitmap(result);
            mImageView.setScaleType(ScaleType.CENTER);
            // mImageView.setBackgroundResource(R.drawable.frame);
        }
    
    
    
    
    
        @Override
          public boolean onTouch(View v, MotionEvent event) {
                // handle touch events here
                ImageView view = (ImageView) v;
                switch (event.getAction() & MotionEvent.ACTION_MASK) {
                    case MotionEvent.ACTION_DOWN:
                        savedMatrix.set(matrix);
                        start.set(event.getX(), event.getY());
                        mode = DRAG;
                        lastEvent = null;
                        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);
                        Log.e("MainActivity","MainActivity down d = "+d);
                        break;
                    case MotionEvent.ACTION_UP:
                    case MotionEvent.ACTION_POINTER_UP:
                        mode = NONE;
                        lastEvent = null;
                        break;
                    case MotionEvent.ACTION_MOVE:
                        if (mode == DRAG) {
                            matrix.set(savedMatrix);
                            float dx = event.getX() - start.x;
                            float dy = event.getY() - start.y;
                            Log.e("TEST","Dx : "+ dx+" Dy : "+ dy);
                            matrix.postTranslate(dx, dy);
                        } else if (mode == ZOOM) {
                            float newDist = spacing(event);
                            if (newDist > 10f) {
                                matrix.set(savedMatrix);
                                float scale = (newDist / oldDist);
                                matrix.postScale(scale, scale, mid.x, mid.y);
                            }
                            if (lastEvent != null && event.getPointerCount() == 3) {
                                newRot = rotation(event);
                                Log.e("MainActivity","MainActivity move d= "+d);
                                float r = newRot - d;
                                float[] values = new float[9];
                                matrix.getValues(values);
                                float tx = values[2];
                                float ty = values[5];
                                float sx = values[0];
                                float xc = (view.getWidth() / 2) * sx;
                                float yc = (view.getHeight() / 2) * sx;
                                matrix.postRotate(r, tx + xc, ty + yc);
                            }
                        }
                        break;
                     default:
                         view.setImageMatrix(matrix);
                         return true;
                }
                view.setImageMatrix(matrix);
                return true;
            }
    
            /**
             * 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);
            }
    
            /**
             * Calculate the degree to be rotated by.
             *
             * @param event
             * @return Degrees
             */
            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);
            }
            // these matrices will be used to move and zoom image
            private static Matrix matrix = new Matrix();
            private static Matrix savedMatrix = new Matrix();
            // we can be in one of these 3 states
            private static final int NONE = 0;
            private static final int DRAG = 1;
            private static final int ZOOM = 2;
            private int mode = NONE;
            // remember some things for zooming
            private PointF start = new PointF();
            private PointF mid = new PointF();
            private float oldDist = 1f;
            private float d = 0f;
            private float newRot = 0f;
            private float[] lastEvent = null;
    
            boolean flag ;
    }
    

    Looking my demo like:-

    Screenshot-1 Screenshot-1

    When zoom-out its looking as below image:- Screenshot-2 Screenshot-2

    What exactly i am looking the solution :- Screenshot-3

    Screenshot-3

    If anyone have idea.Please help me.Thanks in advance...

  • Aashish Bhatnagar
    Aashish Bhatnagar almost 7 years
    thank you for the library it works like charm in square image cropping however I tried doing the rectangular cropping but photoview is ignoring the image bounds