Picasso

picasso

Picasso

Contexte

Quelles sont pour vous les fonctionnalités premières d’une application mobile digne de ce nom ? Ce dont toute nos applications Android sont censés devoir maitriser, mais qui ne sont pas présents de base dans le SDK de Google ?

N’allez pas chercher loin, je parle bien des appels réseau et plus précisément, l’affichage d’une image à partir d’une URL. D’un point de vue utilisateur cette fonctionnalité semble simple, surtout lorsque l’on a l’habitude d’utiliser des technologies web comme HTML, mais sous Android cette simple fonctionnalité se décompose en plusieurs actions à effectuer :

  • Téléchargement de l’image de façon asynchrone depuis une URL
  • Gestion du cache d’une image
  • Réduction de la taille d’une image
  • Insertion de l’image dans une ImageView

Pour avoir déjà essayer de reproduire toutes ses actions à la main et obtenir un résultat fluide tout en gardant un code propre… Je ne peux que vous conseiller de vous tourner vers des librairies qui le feront certainement mille fois mieux que vous 😀

Il existe plusieurs librairies qui vous mâchent le travail, parmi elles je vais vous parler de ma préférée : Picasso.

 A propos

Picasso est une librairie développée par le groupe Square, une communauté de développeurs passionnés d’open-source, qui développe les librairies les plus connues et les plus maintenues de la plateforme Android.

Parmi les contributeurs de Square nous pouvons retrouver le grand Jake Wharton, membre très actif de la communauté Android (si vous ne le connaissez pas encore je vous invite à visiter son compte github : https://github.com/JakeWharton). (Mec t’as trop la classe 😀 )

Parlons technique

Picasso est une librairie très appréciée des développeurs mobiles en vue de sa facilité d’utilisation, mais surtout de ses performances. Regardons ensemble comment fonctionne ce module.

Tout d’abord, il est nécéssaire de l’importer dans notre projet, de préférence en utilisant gradle (je vous invite à suivre le tutoriel maitriser-gradle-partie-1 si vous n’êtes pas habitué à l’outil). Pour cela il vous suffit d’ajouter la ligne suivante à vos dépendances :

compile 'com.squareup.picasso:picasso:2.5.0'

Une fois cette librairie importée, regardons comment l’utiliser. Un des points fort de cette librairie est qu’elle s’utilise directement avec les ImageViews, nul besoin de modifier vos layout en ajoutant des objets bizarres pour la faire fonctionner.

Prenons donc comme exemple la vue suivante :

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- une ImageView de 200dp, pour l'instant sans contenu -->
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:src="#AAA"
        />

</RelativeLayout>

Capture d’écran 2015-02-24 à 23.18.56
Avec Picasso tout se fait du coté Java, regardons un exemple simple d’utilisation, celui fournit sur le site de square :

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

Avouez que ça a l’air simple d’utilisation, il suffit de fournir à Picasso une url et une ImageView, et il fait tout le travail pour nous 😀

Mettons donc ce code dans notre activité principale :

public class MainActivity extends ActionBarActivity {

    ImageView imageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById(R.id.imageView);

        Picasso.with(getBaseContext()).load("http://i.imgur.com/DvpvklR.png").into(imageView);
    }
}

Compilez et lancez le projet sur votre smartphone. Oh ça ne fais rien ?

oops

Attendez, je vous ai bien dit que cette librairie s’occupait de télécharger les images et gère le cache pour nous ? bah il faut qu’on lui en donne les droits, ajoutons au projet les permissions nécessaires pour accéder à internet et sauvegarder les images sur la carte SD :

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Ces lignes sont à ajouter à notre fichier AndroidManifest.xml :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tutosandroidfrance.picassosample" >

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

        </activity>

    </application>

</manifest>

Recompilez, tout devrais marcher comme prévu :

picasso_sample - copie

 

L’image est bien téléchargée, mise en cache et insérée dans l’ImageView. Si vous ne me croyez pas sur parole lorsque je vous dit qu’elle est bien en cache, passez votre smartphone en mode avion et relancez l’application, l’image se chargera bien.

Personnalisation

La principale différence qu’a Picasso par rapport à d’autre librairie de caching se trouve au niveau de la liberté de personnalisation des images reçues qu’elle offre. Une fois l’image téléchargée et mise en cache, Picasso offre la possibilité de la modifier avant de l’afficher dans l’ImageView, ce qui permet par exemple de télécharger une image, lui appliquer un effet de flou, puis de la placer comme arrière plan de notre écran d’accueil. Ces personnalisations se font par le biais d’objets nommée Transformation, et le plus magique dans tout ça, Picasso re-cache l’image transformée, ce qui fait que la transformation n’est effectuée qu’une seule fois, ce qui améliore grandement les performances.

Regardons justement comment faire pour appliquer un effet de flou sur l’image précédemment affichée :

Pour mon algorithme de flou j’utiliserai un code pré-existant et très efficace (un grand merci au développeur PomepuyN d’avoir mit cette classe open-source):

https://github.com/PomepuyN/BlurEffectForAndroidDesign/blob/master/BlurEffect/src/com/npi/blureffect/Blur.java#L21

public class MainActivity extends ActionBarActivity {

    ImageView imageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = (ImageView) findViewById(R.id.imageView);

        Picasso.with(getBaseContext()).load("http://i.imgur.com/DvpvklR.png")
                .transform(new BlurTransformation(this))
                .into(imageView);
    }

    public class BlurTransformation implements Transformation {
        private Context context;

        public BlurTransformation(Context context) {
            this.context = context;
        }

        @Override
        public Bitmap transform(Bitmap source) {
            //Source from : https://raw.githubusercontent.com/PomepuyN/BlurEffectForAndroidDesign/master/BlurEffect/src/com/npi/blureffect/Blur.java
            //Copyright : PomepuyN
            Bitmap result = Blur.fastblur(context,source,10);

            //si l'image a été modifiée, on désalloue la mémoire occupée par l'ancienne Bitmap
            if (result != source) {
                source.recycle();
            }
            return result;
        }

        //la clé va être utilisée lors de la sauvegarde en cache
        @Override
        public String key() {
            return "BlurTransformation";
        }
    }
}

Plutôt simple à mettre en place non ?

picasso_blur_small

Il est possible d’ajouter plusieurs transformation à une image, comme ceci :
(la GrayScaleTransformation n’est pas implémentée, elle est juste donné comme nom d’exemple)

Picasso.with(getBaseContext()).load("http://i.imgur.com/DvpvklR.png")
.transform(new BlurTransformation(this))
.transform(new GrayScaleTransformation()) //niveau de gris
.into(imageView);

Performances

Ce dont il faut se méfier à chaque fois que l’on utilise une librairie de traitement d’image est l’occupation mémoire, physique ou RAM. Demander l’affichage d’une image de 3000×3000 px ne prend pas le même temps que d’afficher une image 400×400 px et surtout vous risquez vite d’atteindre le OutOfMemoryException en utilisant de grosses images.
C’est pourquoi je vous conseille de toujours redimensionner vos images après les avoir téléchargé. Dit comme ça l’opération semble compliquée, mais les développeurs de Square ont pensé à tout, ils nous fournissent plusieurs méthodes permettant de réaliser cette opération, je vais donc vous les lister :

Utiliser une Transformation

public class ResizeTransformation implements Transformation{

    //la largeur voulue
    private int targetWidth;

    public ResizeTransformation(int width) {
        this.targetWidth = width;
    }

    @Override
    public Bitmap transform(Bitmap source) {
        double aspectRatio = (double) source.getHeight() / (double) source.getWidth();
        int targetHeight = (int) (targetWidth * aspectRatio);
        Bitmap result = Bitmap.createScaledBitmap(source, targetWidth, targetHeight, false);
        if (result != source) {
            // Same bitmap is returned if sizes are the same
            source.recycle();
        }
        return result;
    }

    @Override
    public String key() {
        return "ResizeTransformation"+targetWidth;
    }
}

J’ai volontairement mit une petite résolution pour que l’effet soit visible. L’utilisation de cette transformation se fait simplement :

Picasso.with(getBaseContext()).load("http://i.imgur.com/DvpvklR.png")
                .transform(new ResizeTransformation(50))
                .into(imageView);

picasso_resize_transformation_small

Utiliser la méthode Resize()

Picasso fournit une méthode resize, prenant 2 paramètres : hauteur et largeur, qui est plutôt efficace et facile à utiliser, je vous laisse en juger :

Picasso.with(getBaseContext()).load("http://i.imgur.com/DvpvklR.png")
                .resize(50,50)
                .into(imageView);

picasso_resize_small

Nous pouvons remarquer que l’image est maintenant carrée, j’ai intentionnellement mit 50×50 px afin de mettre en avant ce soucis, cette méthode ne réduit pas en gardant les proportions. Afin d’utiliser cette méthode, il est nécessaire de savoir à l’avance le ration de l’image à placer (ex: 16/9 ou 1/1).

Utiliser la méthode fit()

Picasso nous fournit la solution miracle : réduire l’image en fonction de la taille de l’ImageView !
Oui oui c’est possible, cette méthode va attendre que l’ImageView ait acquis sa taille et ensuite va resize l’image téléchargée tout en gardant ses proportions. Pour ce faire il faut utiliser la méthode .fit(), associée à la méthode .centerInside() si vous souhaitez afficher toute l’image, ou .centerCrop() pour découper l’image et remplir l’ImageView.

CenterInside
Picasso.with(getBaseContext()).load("http://i.imgur.com/DvpvklR.png")
                .fit().centerInside()
                .into(imageView);

picasso_centerInside_small

CenterCrop
Picasso.with(getBaseContext()).load("http://i.imgur.com/DvpvklR.png")
                .fit().centerCrop()
                .into(imageView);

picasso_centercrop_small

Cette méthode reste de loin ma préférée, car plus simple à utiliser, plus performante et moins couteuse en mémoire. De plus, elle évite de dégrader l’image, dans les deux méthodes précédentes nous devions écrire la taille précise en pixels alors que celle-ci ajuste directement l’image en fonction de la zone affichée, elle sera donc plus nette.

.fit() est d’ailleurs plus que préconisée par Jake Wharton 😉

 

Vous pourrez trouver toutes mes ressources  à l’adresse suivante : https://github.com/florent37/TutosAndroidFrance/tree/master/PicassoSample 

Et beaucoup d’informations supplémentaires sur Picasso sur leur site officiel :

http://square.github.io/picasso/

Vous savez maintenant comment afficher une image depuis une url, n’hésitez pas à consulter d’autres articles si vous souhaitez découvrir d’autres merveilleuses librairies android.

Un commentaire sur “Picasso

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *