LoginSignup
59
52

More than 3 years have passed since last update.

Migrate Glide v3 to v4 ✈️

Last updated at Posted at 2017-07-02

この記事では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においては

v3
Glide.with(context)
        .load(imageUrl)
        .centerCrop()
        .placeholder(R.drawable.placeholder)
        .error(R.drawable.error)
        .into(targetImageView)

という様にGlide.with()に続けてオプションを指定することができましたが、v4においてはGlide.with()に続けて指定できるオプションは限られています。

v4ではRequestOptionsを使用してその他のオプションを指定します。

v4
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の時と同様に

v4
GlideApp.with(context)
        .load(imageUrl)
        .centerCrop()
        .placeholder(R.drawable.placeholder)
        .error(R.drawable.error)
        .into(targetImageView)

といった具合に続けて書くことができます。
この記事やサンプルプロジェクト内ではGlideAppを使用する形に統一しています。

Resource Typeの指定

Glideを使用すると簡単に指定した形式で画像を読み込むことが可能です。
例えば、v3では

v3
Glide.with(context)
        .load(imageUrl)
        .asBitmap()
        ...

Glide.with(context)
        .load(imageUrl)
        .asGif()
        ...

とすればよかったですね。
しかし、v4においてはこのasXxx()を指定する場所が変わりました。形式の指定はwith()の後に記述しなければなりません。
つまり

v4
GlideApp.with(context)
        .asBitmap()
        .load(imageUrl)
        ...

GlideApp.with(context)
        .asGif()
        .load(imageUrl)
        ...

といった具合に記述する必要があります。
これはGlide v4において、読み込みの形式をより柔軟に指定することが可能になった影響だと考えられます。ライブラリ内で用意されている

  • asDrwable
  • asBitmap
  • asGif
  • asFile

以外にも、.as()を使用することで、任意の形式に変換することが可能になりました。

読み込みのキャンセル

Glideは画像の読み込み、またはキャンセルをよしなにハンドリングしてくれますが、任意のタイミングでキャンセルを行いたいという場合も出てきます。
v3においては

v3
Glide.clear(tragetImageView)

でよかったのですが、v4では

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を使用するのが理想的だと書いてあります。しかし、同じリソースを何度も表示したり様々なサイズで表示したりするアプリや、通信状況の悪い場合を考慮したいアプリなどはDATAALLを試してみると良いようです。

Gifの読み込みにRESOURCEを使用した例です。

読み込みが非常にスムーズですね!

Transformations

Glideを使用することにより、表示する画像を加工することができます。
またGlide Transformationsという有名なライブラリを使用することで、さらに多くのTransformationを簡単に実現することが可能です。もちろんTransformationインタフェースを自分で実装してオリジナルのTransformationを作成することも可能です。

Circle Crop

そんな便利なTransformationですが、v3のGlideライブラリ自身には.centerCrop().fitCenter()の2つのオプションしか含まれていませんでした。

v3
Glide.with(context)
        .load(urlString)
        .centerCrop()
        // .fitCenter() // fitCenterを指定
        // .bitmapTransform(CustomTransformation) // カスタムTransformation
        ...

このように使用できますね。
v4では何が変わったかというと、この2つに加えて.circleCrop()が登場しました。これはその名の通り、画像を円形に切り取ってくれます。

v4でもこれらの使い方は変わりません。

v4
GlideApp.with(context)
        .load(urlString)
        .circleCrop()
        // .centerCrop() // centerCropを指定
        // .fitCenter() // fitCenterを指定
        // .transform(CustomTransformation) // カスタムTransformation
        ...

Multi Transformations

先ほども紹介したGlide Transformationsを見てみると、多くのTransformationがあることが分かります。「あれとこれを一緒に使えたら良いのになあ」なんて思っちゃいますよね?
そう、Glideなら簡単にできちゃうんです!!

実はこれv3の頃からできるんですが、v4ではちょっぴり使い方が変わりました。

v3
Glide.with(context)
        .load(urlString)
        .bitmapTransform(GrayscaleTransformation(context), BlurTransformation(context, 10))
        ...
v4
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が追加されました。
具体的には以下のように記述します。

v4.2
GlideApp.with(context)
        .load(urlString)
        .transforms(GrayscaleTransformation(), BlurTransformation(10))
        ...

v3の時のように記述できるようになりました!!

Custom Transformation

先ほども少し触れましたが、独自のTransformationを作成するにはTransformationインタフェースを実装したクラスを作成する必要があります。
このTransformationインタフェースですが、v3とv4で大きく分けて2つの変更が加わりました。

  1. transformメソッドの引数にContextが加わった
  2. 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にするには、次のように記述します。

v4
GlideApp.with(context)
        .load(imageString)
        .placeholder(R.drawable.image_placeholder)
        .transition(DrawableTransitionOptions.withCrossFade())
        ...

.transition()の引数にDrawableTransitionOptions#withCrossFade()を設定する必要があります。asBitmap()を使用してBitmap形式に変換している場合はBitmapTransitionOptions#withCrossFade()を使用すれば良いです。

クロスフェードのアニメーションにかかる時間を変更したい場合は

v4
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をオンにしたい場合には

v4.2
override fun applyOptions(context: Context, builder: GlideBuilder) {
    builder.setDefaultTransitionOptions(Drawable::class.java, DrawableTransitionOptions.withCrossFade())
            .setDefaultTransitionOptions(Bitmap::class.java, BitmapTransitionOptions.withCrossFade())
    }

というように記述します。

また、defaultのCross Fadeはオンにしたいけど、一部ではオフにしたいという場合には、

v4.2
GlideApp.with(context)
        .load(imageString)
        .placeholder(R.drawable.image_placeholder)
        .transition(GenericTransitionOptions.withNoTransition())
        ...

として、GenericTransitionOptions#withNoTransitionを使用すれば良いです。

Animation

Glideではクロスフェード以外にもスライドインやズームインなど、様々なアニメーションを画像の表示時に指定することが可能です。
Glide v3では.animate()の引数にアニメーションを指定することで実現可能でした。
しかし、v4においてはクロスフェードの時と同様に.transition()を使用してアニメーションを指定するように変更されました。
例えば次のようなズームインアニメーションを指定したい場合には

R.anim.zoom_in
<?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>
v4
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

CircleImageViewShapeImageViewといったImageViewの一部がクロップされているImageViewに対して、.placeholder().thumbnail()で指定した画像とクロスフェードさせようとした場合、読み込んだ画像がそのImageViewの形にクロップされずに表示されてしまいます。

実装は次の通りです。

v4
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でも発生します。
対処方法がいくつかあるので、紹介します。

  1. クロスフェードさせない
    • そもそもクロスフェードが原因なので.dontAnimate()を使用して、クロスフェードをさせなければ問題はないです
  2. .circleCrop()を使用
    • 円形のImageViewの場合はImageViewをクロップするのではなく、表示する画像を.circleCrop()を使用して円形にクロップしてあげれば、良いです。ただし、クロスフェードも効きませんし、placeholder()で指定した画像まではクロップされません。
  3. animationを指定する
    • .transition(DrawableTransitionOptions().transition(animate))のanimateにフェードインのアニメーションを指定してあげることで、厳密には.placeholder()で表示した画像とのクロスフェードとまではいきませんが、それっぽく表示することが可能です。

どうしても一部のImageViewがクロップされているImageViewに対して、どうしてもクロスフェードをかけたいという場合には3の方法で妥協するしかないようです。

実装は次の通りです。

v4
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をオンにする必要があります。

先ほども紹介しましたが、一般的な画像に対してクロスフェードをかけたい場合には

v4
GlideApp.with(context)
        .load(imageString)
        .placeholder(R.drawable.image_placeholder)
        .transition(DrawableTransitionOptions.withCrossFade())
        ...

でクロスフェードをオンにする必要がありました。
しかし、この特殊な場合には

v4
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

API/Behavior changes

  • cross fade transitionの実装がViewAnimationからTransitionDrawableに
  • DecordeFormatを DecodeFormat.ARGB_8888DecodeFormat.RGB_565 に設定している場合、Hardware Bitmapを使用するように

4.2.0

Features

Behavior changes

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

Behavior Changes

4.4.0

Features

4.5.0

Features

Behavior Changes

Breaking Changes

  • @NonNull @Nullable アノテーションが追加されたため、コンパイラプラグインによってはビルドに失敗するかも

Build Changes

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

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

Behavior Changes

Breaking Changes

4.8.0

Features

Deprecations

  • SimpleTargetViewTarget がdeprecatedに

Behavior Changes

Breaking Changes

Build Changes

4.9.0

Features

Deprecations

Behavior Changes

Build Changes

4.10.0

Features

Build Changes

4.11.0

Features

Bugs

参考

59
52
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
59
52