Displaying Images/Photos on Android

posted in: Uncategorized | 0

Introduction

Our Android app has a new feature using the camera to take photos. We can split the feature into two tasks, taking a photo and reviewing a photo by pan-and-zoom.

Take a Photo

To take a photo, we found two methods. The first method uses an Intent to launch the camera app. The second method uses the Camera API to access the hardware directly. For simplicity in our project, we chose the Intent method. Using the Intent to call the camera reduces the amount of time needed to develop taking a photo. By using the Intent approach, we can leverage all the built-in functionality provided by the OS layer.

To start we need to set permission to get access to the camera in the AnroidManifest.xml

The android:required="true" lets the Google Play Store detect if the app should be installed or not, this is optional. If the attribute android:required="true" is omitted, then the app has to check for the feature at runtime. Otherwise, the app will crash when it attempts to access the camera when one does not exist:

The first part of checks that the device has a camera, which may return true even though there is none, and the next part checks for the number of cameras the device has.

Using Intent to call an existing camera app:

The CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE can be any number.

The return from the Intent:

By checking if the data is null 1 prevents the following error when the user press the back button or cancels:

Pan and Zoom to Review a Photo

To review a photo afterward, we came across several issues to overcome. These issues included out of memory and image pan-and-zoom.

Memory

Memory issues encountered using ImageView were the limited amount of RAM in a phone, i.e. Nexus S with 384 MB of available RAM out of 512 MB2.

The following setup prevents an OutOfMemory exception.

To prevent running out of memory with Nexus S, use a byte array to store the image and use BitmapFactory.decodeByteArray to store the photo as a Bitmap. This is recommended over reading the file directly into a Bitmap3.

The file provided to BitmapFactory.decodeByteArray is read using Google Guava4 ByteStreams. Other approaches are documented on Stack Overflow5,6.

Once the byte array is loaded, it can be stored as a Bitmap for ImageView.

The bitmap also needs to be scale down before sending it to ImageView to prevent the following error message:

While the original bitmap is still in memory, this is solved by following the solution mentioned in Stack Overflow:

Zoom

ImageView does not provide the ability to zoom into a image. One option is to use ZoomControls widget and listen to OnTouch events. Another option is to show the image in a WebView. A third option is to use Michael Ortiz TouchImageView 8. We choose TouchImageView because it offered an easy way to get zoom functionality without too much extra coding.

To use TouchImageView in the layout xml do, the following:

Without setting an image in the layout xml but programmatically as discussed above, the following error will show:

After investigating why this happens, we found that onMeasure(...) in TouchImageView has the following code:

When getDrawable() is called it returns null. The conditional check causes the app to crash.

The solution was to include an image source in the layout xml. A blank PNG image did the trick.

The ability to swap out ImageView with TouchImageView in code is an advantage. There is no visible or noticeable lag when zooming in and out.

1 java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1888, result=0, data=null} to activity

2 Nexus S Memory

3 BitmapFactory.decodeFile out of memory with images 2400×2400

4 guava-libraries

5 Elegant way to read file into byte[] array in Java [duplicate]

6 File to byte[] in Java

7 Is it needed to call Bitmap.recycle() after used (in Android)?

8 How can I get zoom functionality for images?

Why would I ever NOT use BitmapFactory’s inPurgeable option?