Nivel inicial10 min de lectura · Publicado el

Picasso: librería de imágenes para Android y Kotlin

Picasso en Android: load() va a por la foto, centerCrop() la recorta a la medida del marco y la caché guarda una copia en un álbum. Picasso: descarga una vezrubentejera.com load()centerCrop()caché
Picasso hace de recadero: con load() va a buscar la foto, con centerCrop() la recorta a la medida del ImageView y guarda una copia en la caché, de donde la saca la próxima vez sin volver a descargarla.

Mostrar una foto de internet en una app Android parece fácil, pero hay mucho trabajo detrás: descargarla en otro hilo para no congelar la pantalla, reducirla para no agotar la memoria, guardarla para no descargarla dos veces y cancelar la descarga si el usuario sale de la pantalla.

Picasso es como un recadero que va a por las fotos, las recorta a la medida del marco y guarda una copia en un álbum (la caché). La próxima vez que le pidas la misma foto, la saca del álbum en lugar de volver a la tienda.

Se puede hacer todo esto sin librería, pero Picasso lo resuelve por ti: hilos, memoria, caché, descarga, redimensionado, imágenes circulares y animaciones.

Añadir Picasso a tu proyecto con Gradle

En 2018 copiaba la dependencia de la web de Picasso, que entonces anunciaba la versión 2.71828.

Y la pegaba al final del bloque dependencies del build.gradle del módulo app:

dependencies {
    // ... las dependencias que ya traía el proyecto
    implementation 'com.squareup.picasso:picasso:2.71828'
}

Hoy, la última versión es la 2.8. En un proyecto actual, el fichero suele llamarse build.gradle.kts (Gradle con Kotlin) y la línea queda así:

dependencies {
    implementation("com.squareup.picasso:picasso:2.8")
}

Pulsa Sync Now y Gradle la descarga. Si quieres entender qué hace Gradle por debajo, lo explico en Gradle en Android.

Permiso de internet

Para descargar imágenes, la app necesita permiso para usar internet. Se declara en el AndroidManifest.xml:

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

Cargar una imagen con Picasso en Kotlin

La idea es siempre la misma: Picasso.get(), qué imagen cargar (load), qué hacerle y dónde ponerla (into).

// En onCreate(): descarga la imagen y la guarda en caché, sin mostrarla.
Picasso.get().load(URL_FLAMENCO).fetch()

// Al pulsar el botón: la pone en los ImageView.
fun cargarImagenes(imageViewArriba: ImageView, imageViewAbajo: ImageView) {
    Picasso.get()
        .load(URL_FLAMENCO)
        .resize(400, 400)   // la reduce a 400 × 400 píxeles
        .centerInside()     // cabe entera dentro de ese tamaño
        .into(imageViewArriba)

    Picasso.get()
        .load(URL_BOSQUE)
        .fit()              // la ajusta al tamaño del ImageView
        .centerCrop()       // rellena el hueco y recorta lo que sobra
        .into(imageViewAbajo)
}

fetch() es la precarga: el recadero trae la foto y la deja en el álbum antes de que la pidas. Si ejecutas la app, la imagen precargada aparece mucho más rápido la primera vez que la que no lo estaba. Si no recuerdas cuándo se llama a onCreate(), lo tienes en el ciclo de vida de las Activities.

Así estaba en la versión original del artículo. Usa las vistas sintéticas de kotlin-android-extensions, un plugin ya retirado que hoy no compila; ahora se usa View Binding:

class PicassoActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_picasso_activity)

        buttonLoader.setOnClickListener{loadImages()}

        Picasso.get().load("https://html.com/wp-content/uploads/flamingo.jpg").fetch()
    }

    private fun loadImages(){
        Picasso
                .get()
                .load("https://html.com/wp-content/uploads/flamingo.jpg")
                .resize(400, 400)
                .centerInside()
                .into(imageViewTop)
        Picasso
                .get()
                .load("https://www.w3schools.com/css/img_forest.jpg")
                .fit()
                .into(imageViewButtom)
    }
}

Cargar desde un fichero o un recurso

load() no solo acepta URLs. También vale para un File, un Uri o un recurso de la app:

Picasso.get().load(fichero).into(imageView)          // un File del dispositivo
Picasso.get().load(R.drawable.foto).into(imageView)  // un recurso de la app

Tipos de alineado: resize, fit, centerInside y centerCrop

Método Qué hace Ten en cuenta
fit() Ajusta la imagen al tamaño del ImageView No se puede combinar con resize() ni usar con fetch()
resize(ancho, alto) La redimensiona a los píxeles que indiques Ahorra memoria con imágenes grandes
centerInside() La encaja entera y centrada; puede sobrar hueco Necesita resize() o fit()
centerCrop() Rellena todo el hueco, centrada, y recorta lo que sobra Necesita resize() o fit()

Un símil: centerInside es meter una foto en un marco sin cortarla, aunque queden bordes; centerCrop es recortarla con tijeras para que llene el marco.

Renderizar una imagen de forma circular

Picasso no trae una imagen circular de serie. Se consigue con una transformación: una clase que recibe la imagen y devuelve otra modificada. En 2018 usaba una clase en Java de un gist de terceros; esta es una versión en Kotlin:

class CircleTransform : Transformation {
    override fun transform(source: Bitmap): Bitmap {
        // 1. Recorta un cuadrado centrado.
        val lado = minOf(source.width, source.height)
        val x = (source.width - lado) / 2
        val y = (source.height - lado) / 2
        val cuadrada = Bitmap.createBitmap(source, x, y, lado, lado)
        if (cuadrada != source) source.recycle()

        // 2. Pinta ese cuadrado dentro de un círculo.
        val redonda = Bitmap.createBitmap(lado, lado, Bitmap.Config.ARGB_8888)
        val pincel = Paint(Paint.ANTI_ALIAS_FLAG).apply {
            shader = BitmapShader(cuadrada, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
        }
        val radio = lado / 2f
        Canvas(redonda).drawCircle(radio, radio, radio, pincel)
        cuadrada.recycle()
        return redonda
    }

    override fun key() = "circle"  // identifica la transformación en la caché
}

Y la aplicas con transform():

Picasso.get()
    .load(URL_BOSQUE)
    .fit()
    .centerCrop()
    .transform(CircleTransform())
    .into(imageView)

Animación de fundido (fade)

Picasso ya aplica un fundido corto cuando la imagen llega de la red o del disco. Si quieres uno más largo, por ejemplo de 3 segundos, desactiva el suyo con noFade() y anima tú el ImageView cuando la imagen esté lista:

imageView.alpha = 0f
Picasso.get()
    .load(URL_BOSQUE)
    .noFade()
    .into(imageView, object : Callback {
        override fun onSuccess() {
            imageView.animate().setDuration(3000).alpha(1f).start()
        }

        override fun onError(e: Exception) {
            imageView.alpha = 1f  // que no se quede invisible si falla
        }
    })

En la versión original usaba fetch() con un Callback y, al terminar, volvía a pedir la imagen con into():

Picasso.get()
        .load("https://www.w3schools.com/css/img_forest.jpg")
        .fetch(object:Callback{
            override fun onSuccess() {
                imageViewButtom.alpha = 0f
                Picasso.get()
                        .load("https://www.w3schools.com/css/img_forest.jpg")
                        .fit()
                        .into(imageViewButtom)
                imageViewButtom.animate().setDuration(3000).alpha(1f).start()
            }

            override fun onError(e: Exception?) {

            }

        })

La versión nueva hace una sola petición, quita el fundido de Picasso para que no se sumen dos y deja la imagen visible si la descarga falla.

La caché de Picasso

Picasso guarda las imágenes en dos sitios: en memoria, para lo que acabas de ver, y en disco, en la carpeta picasso-cache dentro de la caché de la app.

Ver y borrar la caché desde Device Explorer

Abre View > Tool Windows > Device Explorer en Android Studio (en versiones antiguas se llamaba Device File Explorer), o pulsa su botón en la barra lateral.

En el ejemplo, la carpeta de la aplicación está dentro de data/data/com.rubentejera.seccion4cursokotlin. Dentro, en cache/picasso-cache, están las imágenes precargadas:

Device File Explorer de Android Studio con un Xiaomi Mi Max 2: dentro de com.rubentejera.seccion4cursokotlin, la carpeta cache/picasso-cache contiene dos ficheros de imagen y un fichero journal

Para borrar la caché, borra la carpeta picasso-cache (clic derecho > Delete).

Saltarse la caché desde el código

Si una imagen cambia en el servidor pero conserva la URL, puedes pedir a Picasso que no use la caché para esa petición:

Picasso.get()
    .load(URL_BOSQUE)
    .memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)  // ni lee ni guarda en memoria
    .networkPolicy(NetworkPolicy.NO_CACHE)                       // no lee la caché de disco
    .into(imageView)

O borrar de la memoria una imagen concreta con Picasso.get().invalidate(URL_BOSQUE).

La alternativa hoy: Coil o Glide

Si empiezas un proyecto, no elijas Picasso. La documentación oficial de Android propone estas dos librerías para cargar imágenes en Compose:

Coil

  • La que recomienda Square para sustituir a Picasso
  • Pensada para Kotlin, corrutinas y Compose
  • Versión 3.6.3; Android 6.0 (API 23) o superior

Glide

  • Mantenida, con versiones nuevas cada cierto tiempo
  • API parecida a la de Picasso
  • Versión 5.0.9; Android 6.0 (API 23) o superior

Coil con vistas (ImageView)

dependencies {
    implementation("io.coil-kt.coil3:coil:3.6.3")
    implementation("io.coil-kt.coil3:coil-network-okhttp:3.6.3")
}

El mismo ejemplo de la imagen redonda con fundido de 3 segundos:

imageView.load(URL_BOSQUE) {
    crossfade(3000)                              // fundido de 3 segundos
    transformations(CircleCropTransformation())  // recorte circular de serie
}

Fíjate en que Coil trae el recorte circular incluido y detecta solo cómo ajustar la imagen al ImageView.

Coil con Jetpack Compose

dependencies {
    implementation("io.coil-kt.coil3:coil-compose:3.6.3")
    implementation("io.coil-kt.coil3:coil-network-okhttp:3.6.3")
}
AsyncImage(
    model = "https://example.com/image.jpg",
    contentDescription = "Puente en un bosque",
)

Glide

dependencies {
    implementation("com.github.bumptech.glide:glide:5.0.9")
}
Glide.with(imageView)
    .load(URL_BOSQUE)
    .circleCrop()
    .transition(DrawableTransitionOptions.withCrossFade(3000))
    .into(imageView)

Equivalencias para migrar

Picasso Coil Glide
Picasso.get().load(url).into(iv) iv.load(url) Glide.with(iv).load(url).into(iv)
.transform(CircleTransform()) transformations(CircleCropTransformation()) .circleCrop()
Fundido con Callback crossfade(3000) .transition(DrawableTransitionOptions.withCrossFade(3000))
.fit() Automático Automático según el scaleType

Coil tiene una guía oficial para migrar desde Picasso y Glide con más casos.

Una librería obsoleta no se rompe de golpe: se queda atrás poco a poco.

Siguiente paso

  1. Busca com.squareup.picasso en tus ficheros de Gradle para saber si tu app lo usa.
  2. Si lo usa, sube a la 2.8 mientras planificas el cambio.
  3. Migra una pantalla a Coil, comprueba que se ve igual y sigue con las demás.

Si estás empezando con Android y Kotlin, el punto de partida es el Hola Mundo con Android Studio y Kotlin.

Fuentes

Comprobado el 7 de noviembre de 2026:

Los ejemplos en Kotlin de Picasso 2.8, Coil 3.6.3 (vistas) y Glide 5.0.9 se han compilado con kotlinc 2.4.21 contra las librerías publicadas en Maven Central, pero no se han ejecutado en un dispositivo. El ejemplo de Compose es el de la documentación oficial de Coil. La captura es de la versión original del artículo (2018), hecha con Picasso 2.71828 en un Xiaomi Mi Max 2 con Android 7.1.1.