In this post, I am going to discuss how to efficiently work with images in android without impacting performance/speed of application. I will be using a popular image loading library called Picasso.
What I will discuss/Implement:
1) How to load many images (from web or phone’s memory) efficiently in a single activity (just like profile images in WhatsApp chat screen).
2) Crop square/rectangular images to make circular thumbnails.
Picasso:
Picasso is open source, powerful image download and caching library for Android. It is created and maintained by Square.
http://square.github.io/picasso/
https://github.com/square/picasso
Advantages:
1) It simplifies the process of loading images from external URLs and display in the application. Downloading an image from external URLs needs a larger amount of code to achieve this via android networking API’s. But by using Picasso, it can be achieved with few lines of code.
2) While working with many images, implementing image caching logic in order to provide a seamless user experience is a necessity. Picasso provides automatic image caching.
3) Provides methods for custom transformation (eg circular) for more advanced effects. Image transformation is very memory costly in android leading to OutOfMemoryException. Picasso deals with all this.
4) Provides methods for resizes images to better fit into layouts and to reduce memory size.
Installation:
If using eclipse as development IDE, then download Picasso-2.4.0.jar from http://square.github.io/picasso/ into application lib folder.
If using Android Studio IDE, then add a dependency in the build.gradle file of the project.
dependencies {
………
compile ‘com.squareup.picasso:2.5.2’
………
}
Example :
1) Create a new project in Eclipse or Android Studio. While doing so, choose a blank activity.
2) Open the layout file for main activity and add following ImageView to the layout.
<ImageView
android:layout_width="@dimen/img_width"
android:layout_height="@dimen/img_height"
android:id="@+id/imageView"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" />
3) Navigate to MainActivity file and add following lines of code in onCreate method.
ImageView imageView = (ImageView) findViewById(R.id.imageView);
Picasso.with(context)
.load(url")
.into(imageView);
In the first line, we get a reference to the ImageView instance in the layout file. We then load an image into the image view using the Picasso library. We first specify the context by calling with() and passing in the context. We then call the load() method and supply in it with the location of the image. Finally, we tell Picasso where it should display the image when it’s fetched by calling into() and pass in the image view object.
Load() method support resources, URL, and files as image sources
Picasso.with(context).load(R.drawable.carImage).into(imageView);
Picasso.with(context).load(Web URL).into(imageView);
Picasso.with(context).load(new File(image_path)).into(imageView);
Placeholder and Error Fallback:
For any real-time application, we must think of all possible cases. Picasso provides placeholder and error fallback for ImageView. Placeholder is an image that will be shown before the image is loaded. Error fallback will be shown if, there is an error while downloading/loading image. Both fallback and placeholder are optional.
Picasso.with(this)
.load(URL)
.placeholder(R.drawable.placeholder_image); // optional
.error(R.drawable.fallback_image) // optional
.into(imageView);
Image Resize and rotating:
Picasso offers resizeDimen() and rotate() for resizing image to the size of ImageView while maintaining aspect ratio and rotating.
Picasso.with(this)
.load(URL)
.placeholder(R.drawable.placeholder_image) // optional
.error(R.drawable.fallback_image) // optional
.resizeDimen(R.dimen.image_width, R.dimen.image_height) // optional
.rotate(90) // optional
.into(imageView);
Transformations:
Custom transformation on images can be done using Bitmaps in android. But bitmaps are very memory consuming and slow down application especially when a lot of images needs to be loaded into a single activity. This forces us to implement memory caching and lazy loading technique leading to lots of code writing.
Picasso provides transform() function in which we can pass custom transformation to be applied to images. Here I am going transform a rectangular image into a circular thumbnail (many applications use the circular image for showing profile pictures like WhatsApp, Viber).
centreCrop() crop the image from its centre.
Picasso.with(this)
.load(URL)
.placeholder(R.drawable.placeholder_image) // optional
.error(R.drawable.fallback_image) // optional
.transform(new RoundedTransformation(R.dimen.radius, R.dimen.margin)) // optional
.resizeDimen(R.dimen.image_width, R.dimen. image_height) // optional
.centerCrop() // optional
.into(imageView);
In RoundedTransformation() value of radius should be more than half the size of image width or height (whichever is smaller).
Add following file in project’s src package. This RoundedTransformation.java contain code for rounded transformation.
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
public class RoundedTransformation implements com.squareup.picasso.Transformation {
private final int radius;
private final int margin;
public RoundedTransformation(final int radius, final int margin) {
this.radius = radius;
this.margin = margin;
}
@Override
public Bitmap transform(final Bitmap source) {
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() - margin, source.getHeight() - margin), radius, radius, paint);
if (source != output) {
source.recycle();
}
return output;
}
@Override
public String key() {
return "rounded";
}
}
These transformations will occur in the same background thread used to decode the original source bitmap and the final result will be stored in memory. This means you can store different transformations of the same source bitmap for future use.
4) Add Internet permission
Add following line in the manifest file of the project.
< uses-permission android:name=”android.permission.INTERNET” />
5) Finally, build and run the project.