この記事ではGlide 4.0.0 RC1を使用しています。
Glide 4.0.0の正式リリースの際には仕様が変更される場合がありますので、ご注意ください。
Glide 4.2.0での動作を確認していますが、今後の仕様変更にはご注意ください。
はじめに
5月18日にGlide 4.0.0のプレリリース版である、Glide 4.0.0 RC0がリリースされました。
そして、先日6月21日にはRC1がリリースされています。
8月2日にGlide 4.0.0が、9月2日にGlide 4.1.0が、10月4日にGlide 4.2.0がリリースされています。
@ryugooさんが「画像読み込みライブラリ Glide の v4 を試してみる」というGlide v4に関する記事を書いて下さっていますが、実際に試してみたところほかにもTipsがいくつか見つかったので、ここにまとめておこうと思います。
Glide v4の導入の仕方などはこの記事では紹介しませんので、先ほど紹介した@ryugooさんの記事を参考にしてみてください。
サンプルプロジェクト
Glide v3とv4のそれぞれを使用したサンプルアプリを作成したので、ぜひ併せて使ってみてください。
モジュールを切り替えることで、v3とv4を切り替えられるようになっています。また、アプリ内でも簡単にコードを見れるようにしていますので、活用してもらえると嬉しいです。
この記事で紹介する内容以外のことも実装しているので、Glideの入門にも使えるかと思います。
このサンプルプロジェクトおよび、記事内ではKotlinを主に使用しています。
追記
サンプルプロジェクト内にGlide 4.2.0対応を行ったモジュールを追加しました。
基本的な使い方
先ほど紹介した「画像読み込みライブラリ Glide の v4 を試してみる」の中でも紹介されていますが、v3とv4の大きな違いは、Glide
に加えてGlideApp
というクラスを使用できるようになったことです。
v3においては
Glide.with(context)
.load(imageUrl)
.centerCrop()
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.into(targetImageView)
という様にGlide.with()
に続けてオプションを指定することができましたが、v4においてはGlide.with()
に続けて指定できるオプションは限られています。
v4ではRequestOptions
を使用してその他のオプションを指定します。
val options = RequestOptions()
.centerCrop()
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
Glide.with(context)
.load(imageUrl)
.apply(options)
.into(targetImageView)
ただ、複数のtargetに対して同じオプションを適用する場合には便利ですが、targetが単体だったり、複数のtargetに異なるオプションを適用する場合には、なんだか不便な様にも思えます。
そこで、GlideApp
というクラスを使用します。このクラスはv4の大きな特徴と言えるannotation processingで生成されるクラスです。こちらを使用するとv3の時と同様に
GlideApp.with(context)
.load(imageUrl)
.centerCrop()
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.into(targetImageView)
といった具合に続けて書くことができます。
この記事やサンプルプロジェクト内ではGlideApp
を使用する形に統一しています。
Resource Typeの指定
Glideを使用すると簡単に指定した形式で画像を読み込むことが可能です。
例えば、v3では
Glide.with(context)
.load(imageUrl)
.asBitmap()
...
Glide.with(context)
.load(imageUrl)
.asGif()
...
とすればよかったですね。
しかし、v4においてはこのasXxx()
を指定する場所が変わりました。形式の指定はwith()
の後に記述しなければなりません。
つまり
GlideApp.with(context)
.asBitmap()
.load(imageUrl)
...
GlideApp.with(context)
.asGif()
.load(imageUrl)
...
といった具合に記述する必要があります。
これはGlide v4において、読み込みの形式をより柔軟に指定することが可能になった影響だと考えられます。ライブラリ内で用意されている
- asDrwable
- asBitmap
- asGif
- asFile
以外にも、.as()
を使用することで、任意の形式に変換することが可能になりました。
読み込みのキャンセル
Glideは画像の読み込み、またはキャンセルをよしなにハンドリングしてくれますが、任意のタイミングでキャンセルを行いたいという場合も出てきます。
v3においては
Glide.clear(tragetImageView)
でよかったのですが、v4では
GlideApp.with(context)
.clear(targetImageView)
とwith()
の後に呼ばなければいけなくなりました。
v4では画像のリクエストをActivityやFragment単位で保持するようになったためです。
そのため、clear()
を行う際にはwith()
で指定したContextのライフサイクルに注意しておく必要がありそうです。
Diskへのキャッシュ
GlideはmemoryやDiskへ一度読み込んだ画像をキャッシュしてくれます。これによって、再表示する際にわざわざもう一度画像リソースの全てを読み込む必要がなくなり、画像をより早く表示することが可能です。
キャッシュ先はmemoryとDiskの2つがありますが、GlideではどのレベルまでリソースをDiskにキャッシュするか指定することができます。
まずv3の方からおさらいしましょう。
-
DiskCacheStrategy.NONE
- 何もキャッシュしない
-
DiskCacheStrategy.SOURCE
- オリジナルの解像度のデータだけキャッシュ
-
DiskCacheStrategy.RESULT
(default)- 解像度を落としたtarget内に表示されるリソースのみをキャッシュ
-
DiskCacheStrategy.ALL
- 全ての解像度のリソースをキャッシュ
これに対し、v4では次のように変更されました。
-
DiskCacheStrategy.NONE
- 何もキャッシュしない
-
DiskCacheStrategy.DATA
- デコード前に回収されたデータをキャッシュ
-
DiskCacheStrategy.RESOURCE
- デコードされたリソースをキャッシュ
-
DiskCacheStrategy.ALL
- 読み込む画像がローカルにある場合は、デコードされた後のリソースをキャッシュ
- 読み込む画像がリモートにある場合は、デコードされたリソースとデコード前に回収されたデータをキャッシュ
-
DiskCacheStrategy.AUTOMATIC
(default)- 状況に合わせて、キャッシュの方法を決定する
ここでデータとリソースという言葉を使い分けていますが、データというのが画像を取ってくる前のあらゆるデータ、リソースというのが画像を取得した後の必要な部分だけのデータだと考えてもらえると分かりやすいかと思います。
JavaDocsには、ほとんどの場合にはRESOURCE
を使用するのが理想的だと書いてあります。しかし、同じリソースを何度も表示したり様々なサイズで表示したりするアプリや、通信状況の悪い場合を考慮したいアプリなどはDATA
やALL
を試してみると良いようです。
Gifの読み込みにRESOURCE
を使用した例です。
読み込みが非常にスムーズですね!
Transformations
Glideを使用することにより、表示する画像を加工することができます。
またGlide Transformationsという有名なライブラリを使用することで、さらに多くのTransformationを簡単に実現することが可能です。もちろんTransformation
インタフェースを自分で実装してオリジナルのTransformationを作成することも可能です。
Circle Crop
そんな便利なTransformation
ですが、v3のGlideライブラリ自身には.centerCrop()
と.fitCenter()
の2つのオプションしか含まれていませんでした。
Glide.with(context)
.load(urlString)
.centerCrop()
// .fitCenter() // fitCenterを指定
// .bitmapTransform(CustomTransformation) // カスタムTransformation
...
このように使用できますね。
v4では何が変わったかというと、この2つに加えて.circleCrop()
が登場しました。これはその名の通り、画像を円形に切り取ってくれます。
v4でもこれらの使い方は変わりません。
GlideApp.with(context)
.load(urlString)
.circleCrop()
// .centerCrop() // centerCropを指定
// .fitCenter() // fitCenterを指定
// .transform(CustomTransformation) // カスタムTransformation
...
Multi Transformations
先ほども紹介したGlide Transformationsを見てみると、多くのTransformationがあることが分かります。「あれとこれを一緒に使えたら良いのになあ」なんて思っちゃいますよね?
そう、Glideなら簡単にできちゃうんです!!
実はこれv3の頃からできるんですが、v4ではちょっぴり使い方が変わりました。
Glide.with(context)
.load(urlString)
.bitmapTransform(GrayscaleTransformation(context), BlurTransformation(context, 10))
...
GlideApp.with(context)
.load(urlString)
.transform(MultiTransformation(GrayscaleTransformation(), BlurTransformation(10)))
...
コードを見てお気づきだと思いますが、v4では複数のTransformationを指定する際にはMultiTransformation
クラスのインスタンスを.transform()
に渡してあげる必要があります。
ちょっと不便になったなあと感じる方もいるかもしれませんが、その代わりに(と言ってはなんですが)、Transformation
インタフェースのtransform
メソッドの引数にContext
が加わったことにより、カスタムTransformationのコンストラクタでContext
を渡さなくてもよくなりました!
結局記述量はそれほど変わらないですね 笑
追記
Glide 4.1.0で、Multiple Transformationsのshortcutが追加されました。
具体的には以下のように記述します。
GlideApp.with(context)
.load(urlString)
.transforms(GrayscaleTransformation(), BlurTransformation(10))
...
v3の時のように記述できるようになりました!!
Custom Transformation
先ほども少し触れましたが、独自のTransformationを作成するにはTransformation
インタフェースを実装したクラスを作成する必要があります。
このTransformation
インタフェースですが、v3とv4で大きく分けて2つの変更が加わりました。
-
transform
メソッドの引数にContext
が加わった -
getId()
メソッドの代わりにupdateDiskCacheKey(MessageDigest messageDigest)
を実装しなければならなくなった
これらの変更により、先ほどから紹介しているGlide TransformationsをGlide v4で使用するには、使用したいカスタムTransformationを自分でv4でのTransformation
に適応させる必要があります。
ここでは詳しい解説はしませんが、サンプルアプリ内ではv4用に適応させたTransformationがいくつかあるので参考にしてみてください。
Glide Transformationsの方もおそらくGlide v4の正式リリースに合わせてv4向けに修正されるのではないでしょうか。
Transition
Glideを使用することで、表示する画像とplaceholderをクロスフェードさせたり、画像の表示アニメーションを設定することが可能になります。
v3とv4ではTransition周りに多くの変更が加わりました。これまでv3に慣れ親しんだ方はv4を使ってみて結構困惑するかもしれません。
Cross Fade
まず一つ目の大きな変更は、v3ではplaceholderと表示する画像のクロスフェードがデフォルトでONになっていましたが、v4ではデフォルトのクロスフェードはOFFになりました。
v4でクロスフェードをONにするには、次のように記述します。
GlideApp.with(context)
.load(imageString)
.placeholder(R.drawable.image_placeholder)
.transition(DrawableTransitionOptions.withCrossFade())
...
.transition()
の引数にDrawableTransitionOptions#withCrossFade()
を設定する必要があります。asBitmap()
を使用してBitmap形式に変換している場合はBitmapTransitionOptions#withCrossFade()
を使用すれば良いです。
クロスフェードのアニメーションにかかる時間を変更したい場合は
GlideApp.with(context)
.load(imageString)
.placeholder(R.drawable.image_placeholder)
.transition(DrawableTransitionOptions.withCrossFade(1000)) // default is 300
...
という風にwithCrossFade()
の引数にミリ秒で時間を指定します。
追記
Glide 4.1.0でdefaultのTransitionsを設定できるようになりました。
設定はAppGlideModule
を継承しているクラスで行います。
defaultでCross Fadeをオンにしたい場合には
override fun applyOptions(context: Context, builder: GlideBuilder) {
builder.setDefaultTransitionOptions(Drawable::class.java, DrawableTransitionOptions.withCrossFade())
.setDefaultTransitionOptions(Bitmap::class.java, BitmapTransitionOptions.withCrossFade())
}
というように記述します。
また、defaultのCross Fadeはオンにしたいけど、一部ではオフにしたいという場合には、
GlideApp.with(context)
.load(imageString)
.placeholder(R.drawable.image_placeholder)
.transition(GenericTransitionOptions.withNoTransition())
...
として、GenericTransitionOptions#withNoTransition
を使用すれば良いです。
Animation
Glideではクロスフェード以外にもスライドインやズームインなど、様々なアニメーションを画像の表示時に指定することが可能です。
Glide v3では.animate()
の引数にアニメーションを指定することで実現可能でした。
しかし、v4においてはクロスフェードの時と同様に.transition()
を使用してアニメーションを指定するように変更されました。
例えば次のようなズームインアニメーションを指定したい場合には
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
android:duration="@android:integer/config_longAnimTime"
android:fromXScale="0.1"
android:fromYScale="0.1"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1"
android:toYScale="1" />
</set>
GlideApp.with(context)
.load(imageString)
.placeholder(R.drawable.image_placeholder)
.transition(DrawableTransitionOptions().transition(R.anim.zoom_in))
...
という風に指定しなければいけません。
クロスフェードの時との違いは、DrawableTransitionOptions
クラスをインスタンス化し、そのtransition
メソッドにアニメーションを指定するという点です。
クロスフェードの時と同様、asBitmap()
を指定している場合にはBitmapTransitionOptions
クラスに置き換えてください。
また、xmlでのanimation以外にもViewPropertyTransition#Animator
インタフェースを実装したアニメーションを引数に指定することも可能です。
Transitionのまとめ
上で2つのアニメーションの方法を見て気づいた方もいると思いますが、v3における.crossFade()
オプションと.animate()
オプションは、v4において.transition()
オプションに集約されました。
v3とv4でのそれぞれのオプションの対応は次のようになると考えてもらえると良いと思います。
v3 | v4 |
---|---|
.crossFade() |
.transition(DrawableTransitionOptions.withCrossFade()) |
.animate(animate) |
.transition(DrawableTransitionOptions().transition(animate)) |
例外的なTransition
いつくかのパターンにおいて上で紹介した方法ではtransitionが期待した動作をしてくれない場合があるので、そちらも紹介しておきます。
特殊なImageView
CircleImageView
やShapeImageView
といったImageViewの一部がクロップされているImageViewに対して、.placeholder()
や.thumbnail()
で指定した画像とクロスフェードさせようとした場合、読み込んだ画像がそのImageViewの形にクロップされずに表示されてしまいます。
実装は次の通りです。
GlideApp.with(this)
.load(photoList[0].getPhotoUrl())
.placeholder(R.drawable.image_placeholder)
.circleCrop()
.transition(DrawableTransitionOptions.withCrossFade())
.into(circleImageView)
GlideApp.with(this)
.load(photoList[1].getPhotoUrl())
.placeholder(R.drawable.image_placeholder)
.transition(DrawableTransitionOptions.withCrossFade())
.into(diamondImageView)
この問題はGlide v3から残っている問題で、v4でも発生します。
対処方法がいくつかあるので、紹介します。
- クロスフェードさせない
- そもそもクロスフェードが原因なので
.dontAnimate()
を使用して、クロスフェードをさせなければ問題はないです
- そもそもクロスフェードが原因なので
-
.circleCrop()
を使用- 円形のImageViewの場合はImageViewをクロップするのではなく、表示する画像を
.circleCrop()
を使用して円形にクロップしてあげれば、良いです。ただし、クロスフェードも効きませんし、placeholder()
で指定した画像まではクロップされません。
- 円形のImageViewの場合はImageViewをクロップするのではなく、表示する画像を
- animationを指定する
-
.transition(DrawableTransitionOptions().transition(animate))
のanimateにフェードインのアニメーションを指定してあげることで、厳密には.placeholder()
で表示した画像とのクロスフェードとまではいきませんが、それっぽく表示することが可能です。
-
どうしても一部のImageViewがクロップされているImageViewに対して、どうしてもクロスフェードをかけたいという場合には3の方法で妥協するしかないようです。
実装は次の通りです。
GlideApp.with(this)
.load(photoList[0].getPhotoUrl())
.placeholder(R.drawable.image_placeholder)
.transition(DrawableTransitionOptions().transition(android.R.anim.fade_in))
.into(circleImageView)
GlideApp.with(this)
.load(photoList[1].getPhotoUrl())
.placeholder(R.drawable.image_placeholder)
.transition(DrawableTransitionOptions().transition((android.R.anim.fade_in)))
.into(diamondImageView)
また、補足として、v3において3の方法を使用する場合には.asBitmap()
でBitmapに変換した後に.animate
でanimationを指定する必要がありましたが、v4ではBitmapに変換する必要はなくなったようです。
特殊な画像の読み込み
読み込む画像の一部が透明であったり、読み込む画像がplaceholderとして表示する画像のサイズよりも小さい場合にクロスフェードを指定すると、読み込んだ画像の後ろにplaceholderとして表示した画像が残ってしまうという現象が起きます。
この現象はv3では起こりませんが、v4では発生してしまいます。
この方法を解決するにはちょっと特殊な方法でCrossFadeをオンにする必要があります。
先ほども紹介しましたが、一般的な画像に対してクロスフェードをかけたい場合には
GlideApp.with(context)
.load(imageString)
.placeholder(R.drawable.image_placeholder)
.transition(DrawableTransitionOptions.withCrossFade())
...
でクロスフェードをオンにする必要がありました。
しかし、この特殊な場合には
GlideApp.with(context)
.load(imageString)
.placeholder(R.drawable.image_placeholder)
.transition(DrawableTransitionOptions().crossFade(
DrawableCrossFadeFactory.Builder()
.setCrossFadeEnabled(true)
.build()
))
// or
// .transition(DrawableTransitionOptions().transition(android.R.anim.fade_in))
...
というように、明示的にクロスフェードを使用できるよう設定する必要があります。
また、厳密にはクロスフェードとは異なりますが、フェードインアニメーションを使用することで、似たような挙動を実現することも可能です。
おわりに
これまで見ていただいた通り、Glide v3とv4では変更点が多くあり、マイグレーションが必須となります。実際にマイグレーションをしてみて、つまづいた点を中心にまとめてみました。
この記事で紹介した変更点が全てではなく、他にもannotation processingを使用したGlideExtension
など魅力的な変更もあります。
Glide 4.0.0の正式リリースももう間も無くかと思うので、(8月2日にGlide 4.0.0が、9月2日にGlide 4.1.0が、10月4日にGlide 4.2.0がリリースされています。)
ぜひ皆さんもお試しください!
間違いやもっといい方法あるよなどありましたら、コメントいただけると幸いです🙇
おまけ (Glide 4.1.0以降の主な変更点)
4.1.0 / 4.1.1
https://github.com/bumptech/glide/releases/tag/v4.1.0
https://github.com/bumptech/glide/releases/tag/v4.1.1
Features
- O以上で
Bitmap.Config.HARDWARE
に対応 - Target APIが26に
- デフォルトのtransitionを設定可能に
- ネットワークリクエスト時のタイムアウトを設定可能に
-
trimMemory()
やclearMemory()
を明示的に呼ぶ必要がなくなった - 複数のTransfomationを使用するときに、
MultiTransformation
を使用しなくてよくなった
API/Behavior changes
- cross fade transitionの実装がViewAnimationからTransitionDrawableに
- DecordeFormatを
DecodeFormat.ARGB_8888
かDecodeFormat.RGB_565
に設定している場合、Hardware Bitmapを使用するように
4.2.0
Features
- デフォルトの
Encorder
を変更可能に - transcode時に
Option
を設定可能に
Behavior changes
- Viewサイズの計測方法が変更
4.3.0 / 4.3.1
https://github.com/bumptech/glide/releases/tag/v4.3.0
https://github.com/bumptech/glide/releases/tag/v4.3.1
Features
- external cache directoryが使用できない時にinternal cache directoryを使用する
DiskLruCacheFactory
が追加された - 最初の読み込みに失敗した際に、別の読み込みを開始できる
error()
が追加 - bitmap drawableじゃないリソースのdecodeとtransformが可能に
- thumbnailのthumbnailみたいなものを設定したい時にネストが深くならないよう、
thumbnail()
に複数のRequestBuilder
を設定可能に - request自体にも
Context
を渡すようになり、Glide自身もそのContext
のthemeを使用するように -
Bitmap
やDrawable
自体をload
に渡すことが可能に
Behavior Changes
- gif画像を扱う際の品質とメモリ使用が向上
4.4.0
Features
-
Integer.MAX_VALUE
よりも大きなサイズのキャッシュが可能に - Viewがdetachされた際にclearし、reattachされた際に処理を再開する
clearOnDetach()
が追加 - Viewサイズを正しく計測するために、layoutされるまで待つ
waitForLayout()
が追加
4.5.0
Features
- 全てのリクエストを一時停止するメソッドが利用可能に
- resource内の動画のデコードをサポート
Behavior Changes
- ライブラリの提供形式がデフォルトで
aar
になったため、@aar
指定は不要に - APIレベル 19未満を使用しているデバイスは
MemorySizeCaluculator.isLowMemoryDevice
でtrue
が返されるように
Breaking Changes
-
@NonNull
@Nullable
アノテーションが追加されたため、コンパイラプラグインによってはビルドに失敗するかも
Build Changes
- CompileSdkVersionとTargetSdkVersionが27に
4.6.0 / 4.6.1
https://github.com/bumptech/glide/releases/tag/v4.6.0
https://github.com/bumptech/glide/releases/tag/v4.6.1
Breaking Changes
- 自動生成されるクラスなどに
@NonNull
@Nullable
アノテーションが追加
4.7.0 / 4.7.1
https://github.com/bumptech/glide/releases/tag/v4.7.0
https://github.com/bumptech/glide/releases/tag/v4.7.1
Features
- Uri形式のデータに対応
- スクロール時のパフォーマンスが向上
Behavior Changes
-
RecyclerView
やListView
でpreloaderを使用している場合、最初のスクロール前に次のページをpreloadするように
Breaking Changes
-
android.app.Fragment
の使用がdeprecatedに - depracatedだったtransformationのコンストラクタが削除された
4.8.0
Features
- 一つのリクエストに対して複数の
RequestListener
を設定可能に
Deprecations
-
SimpleTarget
とViewTarget
がdeprecatedに
Behavior Changes
-
RequestManager
によって一時停止されたリクエストが即座に一時停止されるようになり、placeholderの表示が可能に
Breaking Changes
-
pause()
とisPaused()
がRequest
インタフェースから削除された
Build Changes
- annotation processerがAndroidXをサポート
4.9.0
Features
- 自動生成されるAPIを使用せずとも
RequestOption
をRequestBuilder
に設定可能に - Global/Activity/Fragmentのスコープに対応した
RequestListener
が追加 - 不要なoverrideを可能な限りなくすために、
CustomTarget
が追加 - dynamic moduleを使用しているresourceの読み込みをサポート
-
transforms()
の代わりにtransform()
の引数に複数のtransformationを追加できるように -
Animatable2Compat
が追加されたことにより、無限ループしないGIFアニメーションの終了が検知できるように
Deprecations
-
transforms()
がdepracatedに
Behavior Changes
- バックグラウンドでリクエストが開始された
RequestListener
のコールバックがバックグラウンドで呼ばれるように
Build Changes
- incremental annotation processingに対応
4.10.0
Features
-
GlideExecutor
をmockするライブラリが追加 - Guavaの
ListenableFuture
に対応したintegration libraryが追加 - 角丸の各角のradiusを変更できる
GranularRoundedCorners
が追加 - resource isのcache keyがday/nightモードに対応
- メモリのクリアを手動で行えるAPIが追加
- wide gamut color spacesに対応
Build Changes
- TargetSdkVersionが28に
- AndroidXに対応
4.11.0
Features
- Glideの使用頻度が低いアプリケーションでメモリ使用を抑えるためにGlideのスレッドにtimeoutが設定できるように
- キャッシュ分析のためにBitmapPoolの情報が取得可能に
- デフォルト設定以外のDiskCacheStrategyを使っている場合でも、リモートの動画データからデコードが可能に
Bugs
- ExifInterfaceの警告ログが発生してしまう問題を修正