［Android］ 画像読み込みライブラリ Coil でできることまとめ

# Coil の良いところ

[Coil](https://coil-kt.github.io/coil/) は Kotlin コルーチンを利用した Android 用の画像読み込みライブラリです。
公式サイトにもありますが、次のような良いところがあるそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/b64cdc6c-ef00-1e82-f658-dc1be17c2b85.png)

| 特徴 | 内容 |
| ------- | ------- |
| 処理が早い | メモリキャッシュ、ディスクキャッシュ、画像のダウンサンプリング、<br>ビットマップの再利用、画像読み込みリクエストの一時停止やキャンセルなど<br>様々な最適化を含めているので効率がよく早い |
| サイズが軽い | CoilはPicassoとGlide、Frescoと同等の機能を持つが、<br>1500メソッドぐらいのライブラリでサイズが軽い  |
| 簡単に使える | Kotlinの言語機能を使ってAPIをシンプルに使いやすくしている |
| モダンな技術を使っている | Kotlinファーストで、CoroutineやOkHttp、Okio、AndroidXなどの<br>モダンなライブラリを利用して開発している。 |

# Coil のセットアップ

次の内容を `build.gradle(app)` に記述して `Coil` をインストールする。

```xml:build.gradle(app)
dependencies {
    implementation 'io.coil-kt:coil:0.8.0'
                  ︙
}
```

今回はインターネットにアクセスするので、 
次のパーミッションを `AndroidManifest.xml` に記述しておく。

```xml:AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="kaleidot725.coilsample">
    <uses-permission android:name="android.permission.INTERNET" />
                                ︙
</manifest>
```

# Coil で ImageView に画像を読み込む

次のようなレイアウトを定義し、`ImageView` に色々な方法で画像を読み込んでみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/8cf5a837-5e27-9c54-3585-0085e2aca3de.png)

```xml:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_gravity="center"
        android:background="#CCCCCC"/>

    <Button
        android:id="@+id/reload_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right|bottom"
        android:layout_margin="16dp"
        android:text="Reload"/>
</FrameLayout>
```

## ただ単純に画像を読み込む

`imageView`の後に`load`と記述し`url`を指定するだけで画像を読み込める。
`String` だけでなく `HttpUrl` `Url` `File` `DrawableRes Int` `Drawable` `Bitmap` などを指定できる。
あと`ImageView.load` では `Disposable` を返すので、それを使えば読み込みをキャンセルできるようになっている。

```Kotlin:MainActivity.kt
        val url = "https://www.underconsideration.com/brandnew/archives/android_2019_logo_inverse.png"
        val imageView = findViewById<ImageView>(R.id.image_view)
        val reloadButton = findViewById<Button>(R.id.reload_button)
        reloadButton.setOnClickListener {
           val disposable = imageView.load(url)
           disposable.dispose()
        }
```

![Nov-28-2019 00-21-05.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/ce4e1b1d-07a2-05c4-9fa4-96d12bbe65af.gif)

## 画像を読み込むときにクロスフェードする

`crossfade` を `true` にするだけで読み込み時にクロスフェードさせることができる。

```kotlin:MainActivity.kt
        val url = "https://www.underconsideration.com/brandnew/archives/android_2019_logo_inverse.png"
        val imageView = findViewById<ImageView>(R.id.image_view)
        val reloadButton = findViewById<Button>(R.id.reload_button)
        reloadButton.setOnClickListener {
            imageView.load(url) {
                crossfade(true)
            }
        }
```

![Nov-28-2019 00-23-10.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/0d33dded-8a7f-24b9-99d4-70fdc10fe575.gif)

## 画像を読み込むときにクロスフェードの秒数(ms)を指定する

クロスフェードのありなしを指定するだけでなく、
時間を指定することで何ミリ秒かけてクロスフェードするか決められる。

```Kotlin:MainActivity.kt
        val url = "https://www.underconsideration.com/brandnew/archives/android_2019_logo_inverse.png"
        val imageView = findViewById<ImageView>(R.id.image_view)
        val reloadButton = findViewById<Button>(R.id.reload_button)
        reloadButton.setOnClickListener {
            imageView.load(url) {
                crossfade(3000)
            }
        }
```

![Nov-28-2019 00-27-44.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/ec2d85ae-3714-8826-7793-5367bd5d895c.gif)

## プレースホルダー画像を設定する

プレースホルダーを設定できる。
次のように読み込み中、クロスフェード中に画像が表示されるようになる。

```kotlin:MainActivity.kt
        val url = "https://www.underconsideration.com/brandnew/archives/android_2019_logo_inverse.png"
        val imageView = findViewById<ImageView>(R.id.image_view)
        val reloadButton = findViewById<Button>(R.id.reload_button)
        reloadButton.setOnClickListener {
            imageView.load(url) {
                placeholder(R.drawable.placeholder)
                crossfade(3000)
            }
        }
```
![Nov-28-2019 08-45-50.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/86e8cc35-1a00-5091-3b32-15b0f0096ac5.gif)

## エラー画像を設定する

エラー画像を設定できます。
次のように読み込みに失敗したときに画像が表示されるようになる。

```kotlin:MainActivity.kt
        val url = "https://hoge.png"
        val imageView = findViewById<ImageView>(R.id.image_view)
        val reloadButton = findViewById<Button>(R.id.reload_button)
        reloadButton.setOnClickListener {
            imageView.load(url) {
                error(R.drawable.error)
            }
        }
```

![Nov-28-2019 08-52-01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/bf4a8fd9-7d0a-d81a-0e60-f35d15aab9a5.gif)

## 読み込み画像を加工する

Coil では読み込んだ画像を加工する機能がある。
`Blur` `CropCircle` `grayscale` `rouded corners` と4種類の加工ができる。

### Blur

次のようにガウシアンフィルターをかけてぼかしが効いた画像を表示できる。

```kotlin:MainActivity.kt
        val url = "https://www.underconsideration.com/brandnew/archives/android_2019_logo_inverse.png"
        val imageView = findViewById<ImageView>(R.id.image_view)
        val reloadButton = findViewById<Button>(R.id.reload_button)
        reloadButton.setOnClickListener {
            imageView.load(url) {
                transformations(BlurTransformation(context = applicationContext, radius = 5f, sampling = 5f))
            }
        }
```

![Nov-28-2019 08-59-23.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/ccae739f-6c65-f3f8-60f7-f1b9bd4cbde1.gif)

### CropCircle

円形に画像をクロッピングできる。
次のように画像の一部を円形でクロッピングして表示できる。

```MainActivity.kt
        val url = "https://www.underconsideration.com/brandnew/archives/android_2019_logo_inverse.png"
        val imageView = findViewById<ImageView>(R.id.image_view)
        val reloadButton = findViewById<Button>(R.id.reload_button)
        reloadButton.setOnClickListener {
            imageView.load(url) {
                transformations(CircleCropTransformation())
            }
        }
```

![Nov-28-2019 09-14-09.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/0b7d4d33-032a-c8d1-7141-44be8bb2942f.gif)

## Grayscale

次のように画像をグレースケールで表示できる。

```MainActivity.kt
        val url = "https://www.underconsideration.com/brandnew/archives/android_2019_logo_inverse.png"
        val imageView = findViewById<ImageView>(R.id.image_view)
        val reloadButton = findViewById<Button>(R.id.reload_button)
        reloadButton.setOnClickListener {
            imageView.load(url) {
                    transformations(GrayscaleTransformation())
            }
        }
```

![Nov-28-2019 22-41-22.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/dc4cbd54-0f72-1c4a-7bb2-23ba200846e6.gif)

## Rounded Corner

次のように角を丸めた画像を表示できる。
`topRight` `topLeft` `bottomeLeft` `bottomRight` とそれぞれの角の丸めを調整できる。

```MainActivity.kt
        val url = "https://www.underconsideration.com/brandnew/archives/android_2019_logo_inverse.png"
        val imageView = findViewById<ImageView>(R.id.image_view)
        val reloadButton = findViewById<Button>(R.id.reload_button)
        reloadButton.setOnClickListener {
            imageView.load(url) {
                transformations(RoundedCornersTransformation(topRight = 10f, topLeft = 10f, bottomLeft =  10f, bottomRight =  10f))
            }
        
```

![Nov-28-2019 22-50-19.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/45e757a7-3fab-3a1b-5830-f9ce4b68621c.gif)


# Coil では Image Loader でキャッシュ設定など変更できる

次のように Coil では ImageLoader を生成することであらかじめどの設定で画像を読み込むか決められる。
例えば 今まで紹介した機能もここに記載しておきますし、Memory Cache や Bitmap Pooling などの細かな設定もできる。

```MainActivity.kt
        val imageLoader = ImageLoader(applicationContext) {
            crossfade(true)
            placeholder(R.drawable.placeholder)
            error(R.drawable.error)
            availableMemoryPercentage(0.1)
            bitmapPoolPercentage(0.1)
        }
```


次のように作成した `ImageLoader` を `load` の引数に与えることで、
`ImageLoader` に設定した内容を利用して画像を読み込むことができる。

```MainActvitiy.kt
        val imageLoader = ImageLoader(applicationContext) {
            crossfade(true)
            placeholder(R.drawable.placeholder)
            error(R.drawable.error)
            availableMemoryPercentage(0.1)
            bitmapPoolPercentage(0.1)
        }
        val url = "https://notfound.png"
        val imageView = findViewById<ImageView>(R.id.image_view)
        val reloadButton = findViewById<Button>(R.id.reload_button)
        reloadButton.setOnClickListener {
            imageView.load(url, imageLoader)
        }
```

![Nov-28-2019 23-27-44.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/ea0deee7-767d-fd66-7a3e-d923bddd575e.gif)

上記のように `load` の引数に指定するのが面倒なのであれば、
次のように `Coil.setDefaultImageLoader` で作成した
`ImageLoader` をデフォルトとして設定しておくこともできる。

```MainActivity.kt
        Coil.setDefaultImageLoader(ImageLoader(applicationContext) {
            crossfade(true)
            placeholder(R.drawable.placeholder)
            error(R.drawable.error)
            availableMemoryPercentage(0.1)
            bitmapPoolPercentage(0.1)
        })

        val url = "https://notfound.png"
        val imageView = findViewById<ImageView>(R.id.image_view)
        val reloadButton = findViewById<Button>(R.id.reload_button)
        reloadButton.setOnClickListener {
            imageView.load(url)
        }
```

![Nov-28-2019 23-33-09.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/44329b37-5398-25e2-9d44-9e813b0fd419.gif)

# 参考文献

- [Coil | Documents](https://coil-kt.github.io/coil/)
- [Coil | Source](https://github.com/coil-kt/coil/)