Capture and Crop an Image with the Device Camera

Many Android devices are equipped with built-in cameras. In this tutorial, we will work through the basic technique for capturing an image using the Android camera and then cropping it using apps the user already has installed on their device. Along the way, I’ll also show how to account for users whose devices do not support either the image capture or cropping actions.

Create a new Android project in Android Studio. In your main Activity class, add the following import statements after the package declaration:

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

Let’s design the app layout. Open your main XML file, which should be in the “res/layout” folder. Use a Linear Layout as follows:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="akraj.snow.app.MainActivity"
    android:orientation="vertical">

Inside the Linear Layout, add a button as follows:

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Camera"
    android:id="@+id/camera" />

The ID value will allow us to identify the button in Java, so that we can respond to clicks. Again, we use a string we already defined in XML. Finally, after the button, add an Image View:

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/imageView"
    android:src="@mipmap/ic_launcher"
    android:background="@drawable/border"/>

We will place the image captured by the user with their device camera inside this Image View, using the ID value to identify it in Java. We use one of the strings as a content description and a background drawable resource we will create next. If you want the Image View to stretch to fill the available space, alter the width and height attributes to “fill_parent” instead of “wrap_content” – remember that this may make the image displayed appear poor quality.
For the background drawable, create a new XML file in each of your app drawable folders, naming it "border.xml" to match the value we used in the Image View layout section. The easiest way to do this is to create the file in one drawable folder, copy the XML code into it, save it, then copy it into the other drawable folders.
Use the following XML in your new "border" drawable file:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#FFFFFF" />
    <stroke android:width="1dp" android:color="#000000" />
    <padding android:left="1dp" android:top="1dp" android:right="1dp"
        android:bottom="1dp" />
</shape>

Using a drawable background resource is entirely optional, so feel free to omit this part. This is how the app will look when it is launched:
App Interface

In your app’s Activity class, extend the opening class declaration line as follows:

public class MainActivity extends AppCompatActivity implements OnClickListener {
    private ImageView imageView;
    private Button camera;

Inside the Activity “onCreate” method, add the following after the existing code calling the superclass method and setting the content layout

imageView = (ImageView) findViewById(R.id.imageView);
camera = (Button) findViewById(R.id.camera);
//handle button clicks
camera.setOnClickListener(this);

Here we simply instruct the class to handle button clicks. The user will press the button to launch the camera. Now we need to provide an “onClick” method. Add it as follows, after the “onCreate” method:

public void onClick(View v) {
        switch (v.getId()) {
            case R.id.camera:
                // code
                break;
            default:
                break;
        }
    }
}

Inside the “if” statement, we will implement using the camera.

At the top of your class declaration, before the “onCreate” method, add the following instance variables:

//keep track of camera capture intent
final int CAMERA_CAPTURE = 1;
//captured picture uri
private Uri picUri;

We will use the first variable to keep track of the user’s interaction as they navigate to the camera app and back. The second variable will store the URI of the captured image. Inside the “case” statement in your “onClick” method, add the following code to launch the camera Intent, including it in a “try” block:

try {
    //use standard intent to capture an image
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    String imageFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/picture.jpg";
    File imageFile = new File(imageFilePath);
    picUri = Uri.fromFile(imageFile); // convert path to Uri
    takePictureIntent.putExtra( MediaStore.EXTRA_OUTPUT, picUri );
    startActivityForResult(takePictureIntent, CAMERA_CAPTURE);
}

When this code executes, the user’s camera app will start up and they will be able to take a photo. We will handle the user returning from the camera app inside the “onActivityResult” method. From there we will be able to check that the user is returning from this Intent using the “CAMERA_CAPTURE” variable we pass when starting the Activity. However, before we do that we need to handle the situation in which the user device does not support the image capture intent we have attempted to launch here. After the “try” block, add a “catch” as follows:

 catch(ActivityNotFoundException anfe){
    //display an error message
    String errorMessage = "Whoops - your device doesn't support capturing images!";
    Toast.makeText(MainActivity.this, errorMessage, Toast.LENGTH_SHORT).show();
}

Whenever you attempt to launch an Intent outside of your own app, this is a precaution you may wish to take. This is particularly the case with the cropping action which we will be exploring later, however this code pattern is a good habit to adopt in general, as user devices vary greatly. When the user accepts the photo they have captured, they will return to the app.
Screenshot_20160509-150228_1462807685199

We launched the camera app using “startActivityForResult” so we now need to handle the result. Add the “onActivityResult” method after the “onClick” method as follows:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode == RESULT_OK) {
    }
}

Inside the “if” statement, add another to check that we are returning from the camera app, using the variable we passed:

//user is returning from capturing an image using the camera
if(requestCode == CAMERA_CAPTURE){
}

We will also return to the “onActivityResult” method after the user crops their image, so we will add an “else if” later. Inside this “if” statement, add the following code to retrieve the URI of the captured photo:

//get the Uri for the captured image
Uri uri = picUri;
Log.d("picUri", uri.toString());

Now we need to pass this URI to an app that can crop it. We will use a helper method to achieve this, so add the following method call:

//carry out the crop operation
performCrop();

Add the helper method we called after the “onActivityResult” method:

private void performCrop(){
}

Inside this method we are going to call an Intent to perform the crop, so let’s add “try” and “catch” blocks in case the user device does not support the crop operation:

try {
}
catch(ActivityNotFoundException anfe){
    //display an error message
    String errorMessage = "Whoops - your device doesn't support the crop action!";
    Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show();
}

We use the same technique we used when launching the camera Intent. If the user device does not support the cropping Intent, they will see an error message. Inside the “try” block, launch the Intent as follows:

//call the standard crop action intent (the user device may not support it)
Intent cropIntent = new Intent("com.android.camera.action.CROP");
//indicate image type and Uri
cropIntent.setDataAndType(picUri, "image/*");
//set crop properties
cropIntent.putExtra("crop", "true");
//indicate aspect of desired crop
cropIntent.putExtra("aspectX", 1);
cropIntent.putExtra("aspectY", 1);
//indicate output X and Y
cropIntent.putExtra("outputX", 256);
cropIntent.putExtra("outputY", 256);
//retrieve data on return
cropIntent.putExtra("return-data", true);
//start the activity - we handle returning in onActivityResult
startActivityForResult(cropIntent, PIC_CROP);

Here we set various properties for the cropped image, instructing the app to retrieve the resulting image data when the crop is complete. If you want the cropped image dimensions to differ from this, alter the “outputX” and “outputY” lines accordingly. We call the Intent using “startActivityForResult”, so will retrieve the result inside “onActivityResult” again. As with the camera Intent, we pass a variable to keep track of which Intent we are returning from, so add a variable declaration at the top of the class, next to the other instance variables:

//keep track of cropping intent
final int PIC_CROP = 3;

Screenshot_20160509-150243_1462807680074
As well as cropping, the user can select an area of the image.
Screenshot_20160509-150253_1462807680150

Finally we can retrieve the cropped image and display it within the app UI. Inside your “onActivityResult” method, after the “if” statement in which you check for the “CAMERA_CAPTURE” request code, add an “if else” statement checking for the “PIC_CROP” code, in which case we are returning from the crop operation:

//user is returning from cropping the image
else if(requestCode == PIC_CROP){
}

Inside this statement, we can retrieve the returned cropped image as follows:

//get the returned data
Bundle extras = data.getExtras();
//get the cropped bitmap
Bitmap thePic = (Bitmap) extras.get("data");

We now have the user’s cropped image as a Bitmap. Let’s display it in the Image View as follows:

//display the returned cropped image
imageView.setImageBitmap(thePic);

Now the cropped image will appear inside the app UI as soon as the user returns from cropping. Save and run your app on an actual device to test it.
Screenshot_20160509-150259_1462807675387

Add following permission to Manifest.xml file

<uses-feature android:name="android.hardware.camera"
 android:required="true" />
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
 android:maxSdkVersion="23" />
 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

If you want to use images from gallery then follow these steps:

At the top of your class declaration, add the following instance variables:

final int PICK_IMAGE_REQUEST = 2;

Add a new button in main xml file

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Gallery"
    android:id="@+id/gallery" />

Inside the Activity “onCreate” method:

gallery = (Button) findViewById(R.id.gallery);
gallery.setOnClickListener(this);

Add following code in “onClick” method:

case R.id.gallery:
    Intent galleryIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    // Start the Intent
    startActivityForResult(galleryIntent, PICK_IMAGE_REQUEST);
    break;

now add following code to onActivityResult method:

else if(requestCode == PICK_IMAGE_REQUEST){
    picUri= data.getData();
    performCrop();
}

now run your app…
Screenshot_20160509-150335_1462807675215     Screenshot_20160509-150352_1462807668065     Screenshot_20160509-150400_1462807667931     Screenshot_20160509-150406_1462807667719

In this tutorial we have explored the basic capture and crop process within the Android SDK. However, the crop action in particular can be a little unpredictable on the various user devices in operation. Lots of different apps can handle the crop operation, so some developers adopt a more complex algorithm to resolve the user choices, presenting these apps to the user as a list of options to choose from. Whatever apps the user environment provides, the basic process of capture and crop remains the same.
 
 

Download Complete Source Code

 

About the author

Akshay Raj

View all posts

16 Comments

      • how to create a new file ? because after i have save the crop image in the gallery .. i need to call the URI of the image and need to be send to another activity..sorry im a newbie in android ..

        • String filename = "image001.png";
          File sd = Environment.getExternalStorageDirectory();
          File dest = new File(sd, filename);
          //get the returned data
          Bundle extras = data.getExtras();
          //get the cropped bitmap
          Bitmap thePic = (Bitmap) extras.get("data");
          try {
               FileOutputStream out = new FileOutputStream(dest);
               thePic.compress(Bitmap.CompressFormat.PNG, 90, out);
               out.flush();
               out.close();
          } catch (Exception e) {
               e.printStackTrace();
          }
          • thank you for your source code.. later i will try to put in my project .. if i have a problem i will refer you back 🙂 Tq

  • i would like to store the cropped image to gallery . saved but not able to view in the gallery . can you include code for saving cropped image to gallery and later viewing the saved image from the gallery.

    • Run this code when you created image

      private void galleryAddPic() {
          Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
          File f = new File(currentPhotoPath);
          Uri contentUri = Uri.fromFile(f);
          mediaScanIntent.setData(contentUri);
          this.sendBroadcast(mediaScanIntent);
      }
      
  • Camera doesn’t open on my projects, I think I did something wrong but I don’t known help me..