Android Picasso is a powerful image downloading and caching library. In this tutorial, we’ll be discussing and implementing Picasso library in our android application.
Android Picasso
Android Picasso is an image loading/processing library developed and maintained by Square Inc. It’s immensely popular since it often requires just one line of code and has a similar style of coding for each of its features(we’ll be implementing them soon!). To use the android Picasso Library in your Android Studio project, add the following dependency in your build.gradle
file.
1 2 3 |
compile 'com.squareup.picasso:picasso:2.5.2' |
Android Picasso comes with its own set of features such as:
- Resizing and Scaling
- Center Cropping
- Rotation and Transformation
- Setting the Placeholder and Error images
- Fading
- Disk and Memory Caching
- Priority Requests
- Support for Request cancellation and parallel downloading
Android Picasso – Loading image from URL
To load a image from URL in an ImageView using picasso api, following code snippet is commonly used.
1 2 3 |
Picasso.with(context).load("https://cdn.journaldev.com/wp-content/uploads/2016/11/android-image-picker-project-structure.png").into(imageView) |
Android Picasso – Loading a resource
To load a resource (drawable/mipmap):
1 2 3 |
Picasso.with(context).load(R.mipmap.ic_launcher).into(imageView); |
Android Picasso – Loading image from File
To load a file image :
1 2 3 4 |
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Sample.jpg"); Picasso.with(context).load(file).into(imageView); |
Android Picasso Caching
-
- Memory Policy
Picasso by default tries to get an image from the memory first. To prevent this we can add the method noMemoryPolicy()
by calling either or both of the enums MemoryPolicy.NO_CAHE, MemoryPolicy.NO_STORE.
- Memory.NO_CACHE is used to prevent loading the image from the stored cache.
- Memory.NO_STORE is used to not store the image in the cache at all. Typically used if we need the image one single time.
1 2 3 |
Picasso.with(context).load(image_url).memoryPolicy(MemoryPolicy.NO_CACHE).into(imageView); |
1 2 3 |
Picasso.with(context).load(image_url).memoryPolicy(MemoryPolicy.NO_STORE).into(imageView); |
- Network Policy
If the image is not served/prevented from the Memory, Picasso next tries to get it from the Disk Cache.
To skip the disk cache we need to call the method .networkPolicy() with the parameter set as NetworkPolicy.NO_CACHE
Another useful enum is NetworkPolicy.OFFLINE which would check for the image in the cache only. It’ll show the error image(if defined) or blank if the image is not found in the cache.
1 2 3 |
Picasso.with(context).load(image_url).networkPolicy(NetworkPolicy.NO_CACHE).into(imageView); |
Let’s dive into the coding part of android picasso tutorial where we’ll see and implement all the features together.
Android Picasso Example Project Structure
Android Picasso Example Code
The activity_main.xml
is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:tools="https://schemas.android.com/tools" android:id="@+id/activity_main" 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="com.journaldev.picassotutorial.MainActivity"> <Button android:text="Drawable" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:id="@+id/btnDrawable" /> <ImageView android:id="@+id/imageView" android:src="https://www.journaldev.com/13759/@mipmap/ic_launcher" android:layout_width="200dp" android:layout_centerHorizontal="true" android:layout_height="200dp" /> <Button android:text="Placeholder" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnPlaceholder" android:layout_alignBaseline="@+id/btnUrl" android:layout_alignBottom="@+id/btnUrl" android:layout_centerHorizontal="true" /> <Button android:text="URL" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnUrl" android:layout_alignBaseline="@+id/btnDrawable" android:layout_alignBottom="@+id/btnDrawable" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" /> <Button android:text="Error" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnError" android:layout_below="@+id/btnDrawable" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_marginTop="@dimen/activity_horizontal_margin" /> <Button android:text="Callback" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnCallBack" android:layout_alignBaseline="@+id/btnError" android:layout_alignBottom="@+id/btnError" android:layout_alignLeft="@+id/btnPlaceholder" android:layout_alignStart="@+id/btnPlaceholder" /> <Button android:text="Resize" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnResize" android:layout_alignBaseline="@+id/btnCallBack" android:layout_alignBottom="@+id/btnCallBack" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" /> <Button android:text="Rotate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/btnError" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_marginTop="13dp" android:id="@+id/btnRotate" /> <Button android:text="Scale" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnScale" android:layout_alignLeft="@+id/btnCallBack" android:layout_alignStart="@+id/btnCallBack" android:layout_alignBottom="@+id/btnTarget" /> <Button android:text="Targets" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnTarget" android:layout_alignBaseline="@+id/btnRotate" android:layout_alignBottom="@+id/btnRotate" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" /> </RelativeLayout> |
The code for MainActivity.java
is given below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
package com.journaldev.picassotutorial; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; import com.squareup.picasso.Callback; import com.squareup.picasso.Picasso; import com.squareup.picasso.Target; public class MainActivity extends AppCompatActivity implements View.OnClickListener { ImageView imageView; int i = 0; Button btnDrawableImage, btnUrlImage, btnErrorImage, btnPlaceholderImage, btnCallback, btnResizeImage, btnRotateImage, btnScaleImage, btnTarget; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { imageView = (ImageView) findViewById(R.id.imageView); btnDrawableImage = (Button) findViewById(R.id.btnDrawable); btnUrlImage = (Button) findViewById(R.id.btnUrl); btnPlaceholderImage = (Button) findViewById(R.id.btnPlaceholder); btnErrorImage = (Button) findViewById(R.id.btnError); btnCallback = (Button) findViewById(R.id.btnCallBack); btnResizeImage = (Button) findViewById(R.id.btnResize); btnRotateImage = (Button) findViewById(R.id.btnRotate); btnScaleImage = (Button) findViewById(R.id.btnScale); btnTarget = (Button) findViewById(R.id.btnTarget); btnDrawableImage.setOnClickListener(this); btnPlaceholderImage.setOnClickListener(this); btnUrlImage.setOnClickListener(this); btnCallback.setOnClickListener(this); btnResizeImage.setOnClickListener(this); btnErrorImage.setOnClickListener(this); btnRotateImage.setOnClickListener(this); btnScaleImage.setOnClickListener(this); btnTarget.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.btnDrawable: Picasso.with(this).load(R.drawable.image).into(imageView); break; case R.id.btnPlaceholder: Picasso.with(this).load("www.journaldev.com").placeholder(R.drawable.placeholder).into(imageView); break; case R.id.btnUrl: Picasso.with(this).load("https://cdn.journaldev.com/wp-content/uploads/2017/01/android-constraint-layout-sdk-tool-install.png").placeholder(R.drawable.placeholder).into(imageView); break; case R.id.btnError: Picasso.with(this).load("www.journaldev.com").placeholder(R.drawable.placeholder).error(R.drawable.error).into(imageView); break; case R.id.btnCallBack: Picasso.with(this).load("www.journaldev.com").error(R.mipmap.ic_launcher).into(imageView, new Callback() { @Override public void onSuccess() { Log.d("TAG", "onSuccess"); } @Override public void onError() { Toast.makeText(getApplicationContext(), "An error occurred", Toast.LENGTH_SHORT).show(); } }); break; case R.id.btnResize: Picasso.with(this).load(R.drawable.image).resize(200, 200).into(imageView); break; case R.id.btnRotate: Picasso.with(this).load(R.drawable.image).rotate(90f).into(imageView); break; case R.id.btnScale: if (i == 3) i = 0; else { if (i == 0) { Picasso.with(this).load(R.drawable.image).fit().into(imageView); Toast.makeText(getApplicationContext(), "Fit", Toast.LENGTH_SHORT).show(); } else if (i == 1) { Picasso.with(this).load(R.drawable.image).resize(200, 200).centerCrop().into(imageView); Toast.makeText(getApplicationContext(), "Center Crop", Toast.LENGTH_SHORT).show(); } else if (i == 2) { Picasso.with(this).load(R.drawable.image).resize(200, 200).centerInside().into(imageView); Toast.makeText(getApplicationContext(), "Center Inside", Toast.LENGTH_SHORT).show(); } i++; } break; case R.id.btnTarget: Picasso.with(this).load("https://cdn.journaldev.com/wp-content/uploads/2017/01/android-constraint-layout-sdk-tool-install.png").placeholder(R.drawable.placeholder).error(R.drawable.error).into(target); break; } } private Target target = new Target() { @Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { imageView.setImageBitmap(bitmap); } @Override public void onBitmapFailed(Drawable errorDrawable) { imageView.setImageDrawable(errorDrawable); } @Override public void onPrepareLoad(Drawable placeHolderDrawable) { imageView.setImageDrawable(placeHolderDrawable); } }; } |
Essentially in the above code, we’ve implemented a feature of Picasso on each button click.
- Drawable : This button click invokes the most basic feature of Picasso i.e. Loading A drawable image into an ImageView
- Placeholder : A placeholder is commonly used to display a drawable image while the main image gets loaded into the imageview. This is essential in cases when the image takes time to load from the web.
123Picasso.with(this).load("www.journaldev.com").placeholder(R.drawable.placeholder).into(imageView);
A.placeholder()
is relevant only after theload
method. In the above line, since the URl doesn’t fetch any image, the ImageView stays with the placeholder. - URL: To load an image from a URl, the url is enclosed as a String inside the
load()
method - Error : An error drawable is generally used in cases where there’s a failure in loading the image. In this case the interim placeholder image gets replaced by the error drawable that’s placed inside
.error()
method. - Callback : Picasso provides callback methods through which we can keep a check of the status of the image loaded (success/error) and display a text accordingly. We’ve displayed a Toast message for the same when an error occurs as show below.
123456789101112Picasso.with(this).load("www.journaldev.com").error(R.mipmap.ic_launcher).into(imageView, new Callback() {@Overridepublic void onSuccess() {Log.d("TAG", "onSuccess");}@Overridepublic void onError() {Toast.makeText(getApplicationContext(), "An error occurred", Toast.LENGTH_SHORT).show();}}); - Resize : Picasso allows us to use a resize the image before displaying it in an ImageView by invoking the method
resize()
and passing the desired width and height in pixels - Rotate : To rotate an image pass the float value inside the
rotate()
method. The image is rotated in degrees upon it’s anchor point (0,0) - Scale : Resizing an image can cause stretched images. To keep the aspect ratio intact use
centerCrop()
orcenterInside()
with theresize()
method.
fit()
is like a delayed resize(), that reduces the image to fit the ImageView bounds.
Note : fit cannot be used with resize() since it has a built-in resize. centerCrop and centerInside can’t be used without callingresize()
with a positive width and height - Targets : This is another form of callback that’s used as a listener for image loading. Targets are an interface that would return the bitmap image along with its placeholder and error drawable(if defined). We can further customise the bitmap image returned in the method
onBitmapLoaded()
or directly show it in the ImageView
The output of the our android picasso example application in action is shown below.
Android Picasso – .noFade() and .noPlaceholder()
Picasso by default fades in the image inside the imageview to provide a meaningful animation. To remove this android animation invoke the method noFade()
in the code as :
1 2 3 |
Picasso.with(this).load().placeholder(R.drawable.placeholder).error(R.drawable.error).noFade().into(imageView); |
A noPlaceholder()
method is handy when an ImageView handles multiple Picasso calls. Going the conventional way(without noPlaceholder method) would cause every new Picasso call to change the previous image to the placeholder and then to the new image. This can look ugly and this is where noPlaceholder()
comes to our rescue.
Android Picasso – Resuming/Pausing/Cancelling Requests
To do any of the above, we need to first set a tag as :
1 2 3 |
Picasso.with(this).load().tag("Me").into(imageView); |
Resuming, Pausing or Cancelling a request is generally done in ListView/RecyclerView.
1 2 3 4 5 6 |
Picasso.with(this).resumeTag("Me"); Picasso.with(this).pauseTag("Me"); Picasso.with(this).cancelTag("Me"); Picasso.with(this).cancelRequest(imageView); //alternative |
Android Picasso Priority Requests
A request such as the one below has higher chances of getting completed first in a ListView which many multiple requests.
1 2 3 |
Picasso.with(this).load().priority(HIGH).into(imageView); |
priority()
offers three types of Constants : HIGH, NORMAL and LOW
Note: Setting a priority just gives an intended order of request calls. It doesn’t gurantee to strictly follow the order.
This brings an end to android picasso tutorial. You can download the final Android Picasso example project from the link given below.