Android capture and crop an Image
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.
Step 1: Start a New Android Project
Create a new Android project in Eclipse, or your chosen IDE. In your main Activity class, add the following import statements after the package declaration:
01
02
03
04
05
06
07
08
09
10
11
12
| 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; |
These will allow us to carry out image capture, crop and display within the app. Open your application's strings XML file, which you should find in the "res/values" folder. Add the following strings:
1
2
3
| < string name = "intro" >Capture a picture to crop!</ string > < string name = "picture" >Picture</ string > < string name = "capture" >Launch Camera</ string > |
We will use these strings within the user interface.
Step 2: Implement the App Layout
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, which Eclipse may have already provided:
1
2
3
4
5
6
| android:layout_width = "fill_parent" android:layout_height = "fill_parent" android:orientation = "vertical" > </ LinearLayout > |
Inside the Linear Layout, add the following text display:
1
2
3
4
5
6
| < TextView android:layout_width = "fill_parent" android:layout_height = "wrap_content" android:text = "@string/intro" android:layout_margin = "3dp" android:textStyle = "bold" /> |
Here we display some informative text using a string we defined in the strings XML file. After the Text View, add a button as follows:
1
2
3
4
5
| < Button android:id = "@+id/capture_btn" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:text = "@string/capture" /> |
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:
1
2
3
4
5
6
7
| < ImageView android:id = "@+id/picture" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:contentDescription = "@string/picture" android:layout_margin = "5dp" android:background = "@drawable/pic_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 "pic_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 "pic_border" drawable file:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
| android:dither = "true" > < gradient android:startColor = "#99ffffff" android:endColor = "#99ffffff" android:centerColor = "#00000000" android:angle = "90" /> < padding android:left = "10dp" android:top = "10dp" android:right = "10dp" android:bottom = "10dp" /> < corners android:radius = "5dp" /> < stroke android:width = "2dp" android:color = "#ccffffff" /> </ 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:
Step 3: Respond to User Clicks
In your app's Activity class, extend the opening class declaration line as follows:
1
| public class ShootAndCropActivity extends Activity implements OnClickListener { |
Alter the class name to suit your own. Inside the Activity "onCreate" method, add the following after the existing code calling the superclass method and setting the content layout, which Eclipse should have populated:
1
2
3
4
| //retrieve a reference to the UI button Button captureBtn = (Button)findViewById(R.id.capture_btn); //handle button clicks captureBtn.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:
1
2
3
4
5
| public void onClick(View v) { if (v.getId() == R.id.capture_btn) { } } |
Inside the "if" statement, we will implement using the camera.
Step 4: Launch the Camera
At the top of your class declaration, before the "onCreate" method, add the following instance variables:
1
2
3
4
| //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 "if" statement in your "onClick" method, add the following code to launch the camera Intent, including it in a "try" block:
1
2
3
4
5
6
| try { //use standard intent to capture an image Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); //we will handle the returned data in onActivityResult startActivityForResult(captureIntent, 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:
1
2
3
4
5
6
| catch (ActivityNotFoundException anfe){ //display an error message String errorMessage = "Whoops - your device doesn't support capturing images!" ; Toast toast = Toast.makeText( this , errorMessage, Toast.LENGTH_SHORT); toast.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.
Step 5: Retrieve the Captured Image
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:
1
2
3
4
5
| 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:
1
2
3
4
| //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:
1
2
| //get the Uri for the captured image picUri = data.getData(); |
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:
1
2
| //carry out the crop operation performCrop(); |
Step 6: Crop the Captured Image
Add the helper method we called after the "onActivityResult" method:
1
2
3
| 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:
1
2
3
4
5
6
7
8
9
| try { } catch (ActivityNotFoundException anfe){ //display an error message String errorMessage = "Whoops - your device doesn't support the crop action!" ; Toast toast = Toast.makeText( this , errorMessage, Toast.LENGTH_SHORT); toast.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:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
| //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:
1
2
| //keep track of cropping intent final int PIC_CROP = 2 ; |
As well as cropping, the user can select an area of the image.
Step 7: Display the Cropped Image
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:
1
2
3
4
| //user is returning from cropping the image else if (requestCode == PIC_CROP){ } |
Inside this statement, we can retrieve the returned cropped image as follows:
1
2
3
4
| //get the returned data Bundle extras = data.getExtras(); //get the cropped bitmap Bitmap thePic = extras.getParcelable( "data" ); |
We now have the user's cropped image as a Bitmap. Let's display it in the Image View as follows:
1
2
3
4
| //retrieve a reference to the ImageView ImageView picView = (ImageView)findViewById(R.id.picture); //display the returned cropped image picView.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.
Conclusion
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.
Source : http://code.tutsplus.com/
No comments: