After writing the necessary code to display a camera preview in the last article it's now time to actually take a picture and save it on the external storage of the device.
We start by extending the layout of the CameraActivity to include a button for taking a picture. We assign a method to it to be called when the user clicks on the button via the attribute android:onClick. Another option would be to assign an OnClickListener to the view in code. Defining the method in the XML results in less code but has the disadvantage of not being checked by the compiler. Since one of the last releases of the Android SDK the lint tool is able to check the onClick attributes for correctness. So we will use the XML attribute here.
activity_camera.xml |
As we encapsulated the code handling the Camera object inside the CameraFragment class the activity just calls takePicture() on the fragment when the user presses the button.
takePicutre() - CameraActivity.java |
The fragment calls takePicture() on the Camera object. It's possible to pass up to four callbacks to this method: A shutter callback, a raw callback, a postview callback and a jpeg callback. According to the documentation their usage is:
- The shutter callback occurs after the image is captured. This can be used to trigger a sound to let the user know that image has been captured.
- The raw callback occurs when the raw image data is available.
- The postview callback occurs when a scaled, fully processed postview image is available.
- The jpeg callback occurs when the compressed image is available.
We are only interested in the JPEG image. The fragment itself is also the callback so we'll implement the Camera.PictureCallback interface.
Once the picture is taken onPictureTaken() will be called with a byte array. We decode the given byte array and create a Bitmap object and pass it via the CameraFragmentListener interface to our CameraActivity. Later we will use this bitmap to draw the mustaches on it.
onPictureTaken() - CameraFragment.java |
Saving the picture to the external storage
Once the activity receives the bitmap object it needs to do a bunch of things:
- Determine the directory to save the picture to and create it if necessary
- Create a unique file for the picture inside the directory and save the image data to it
- Notify the MediaScanner that we created a new file
- Show a toast that the picture has been saved successfully (For now until we've written the activity to display the taken picture).
Determining the directory
We want to save the picture into a directory on the external storage that is visible for all other applications. By calling Environment.getExternalStoragePublicDirectory() and passing Environment.DIRECTORY_PICTURES we get the public directory for pictures (Available since API Level 8). Inside this directory we'll create a directory with the name of our application (if it doesn't exist already).
onPictureTaken() - CameraActivity.java |
Saving the file
We'll create a file with a unique file name containing a timestamp, e.g.: MUSTACHE_20121031_235959.jpg. The compress() method of the bitmap object is used to save the picture into the file.
onPictureTaken() - CameraActivity.java |
Notifying the MediaScanner
Scanning the SD card for changes is costly. Therefore most Android versions only scan the whole card if the card is re-inserted or was mounted by another device. This seems to be different in different vendor versions of Android but nevertheless you can't assume a file to be seen by other applications (for example the gallery) until it has been scanned by the MediaScanner. For that reason we'll notify the MediaScanner about the file we've created.
onPictureTaken() - CameraActivity.java |
By now we've already created a simple camera application. We can take pictures and they show up in the gallery of the device. Pretty cool so far, huh?
Great stuff. Waiting for more! :)
ReplyDelete