【譯】四步經過Glide實現共享元素無縫轉場效果

原文: How to Use Shared Element Transition with Glide in 4 steps
做者:Bartłomiej Osmałek
Markdown:原文 | 譯文java

讀完這篇文章, 你就會知道使用Glide等圖片加載庫實現共享元素轉場效果,以及如何處理各類可能加載狀態。經過共享轉場動畫,能夠提高應用交互體驗,讓用戶使用起來更愉悅android

共享元素轉場效果是Material Design的一個重要的轉場效果. 若是圖片資源是靜態本地的,那麼實現起來是容易的。可是經過網絡下載圖片而且建立一個無縫的動畫效果就比較棘手了。git

開始以前

此篇文章是在開發Toast App時,對於轉場效果的一個總結. 該app是TOAST – Android 開發者聚會(波蘭最大的Android開發者聚會網站)的客戶端app. app中包含了每一個TOAST事件,按期講座和活動照片。 咱們主要用共享元素轉場做爲頁面切換效果。同時使用Glide來獲取全部的圖片。github

本文中介紹的方法也應適用於其餘圖像加載庫,例如Picasso 或者 Fresco (您須要找到Glide特定功能的相對應的代碼實現). 爲此我製做了一個示例演示程序, 發佈在http://www.javashuo.com/tag/github上. 全部代碼段均來自此示例程序。緩存

咱們使用一組網格圖片做爲開始. 當用戶點擊一張圖片, 會打開一個新的Activity,圖片會裁剪並填充整個屏幕。下面是使用Glide加載圖片的相關代碼:網絡

fun ImageView.load(url: String) {
    Glide.with(this)
            .load(url)
            .apply(RequestOptions.placeholderOf(R.drawable.placeholder))
            .into(this)
}
複製代碼

咱們想要的效果:

第一步: 幼稚的共享過渡

咱們能夠經過添加正確options來建立過渡效果,修改 goToDetails 方法以下:
MainActivity.ktapp

fun goToDetails(url: String, imageView: View) {
    val options = ActivityOptionsCompat.makeSceneTransitionAnimation(this, imageView, imageView.transitionName).toBundle()
    Intent(this, DetailActivity::class.java)
            .putExtra(IMAGE_URL_KEY, url)
            .let {
                startActivity(it, options)
            }
}
複製代碼

如今, 咱們在上面的方法中傳入要共享的view,並設置transitionName這個名稱在每一個activity中必須是惟一的,主視圖和詳情頁面相對應的view的transitionName則要相同,在此爲了簡化,咱們使用圖片url做爲transitionName:
DetailActivity.kt框架

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_detail)
    val url = intent.getStringExtra(IMAGE_URL_KEY)
    detailImage.transitionName = url
    detailImage.load(url)
}
複製代碼

ImageAdapter.ktide

inner class ImageViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    fun bind(url: String) {
        (itemView as ImageView).apply {
            load(url)
            transitionName = url
            setOnClickListener { onClick(url, it) }
        }
    }
}
複製代碼

好了, 咱們有了過渡動畫以下:優化

.

糟糕的是這並非咱們想要的效果🙁

第二步: 推遲過渡效果

Glide須要時間將圖片加載到ImageView. 這就是爲何在onCreate咱們不得不推遲過過渡動畫效果,直到圖片下載完成才能夠開始進行過渡效果: DetailActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    supportPostponeEnterTransition()
    detailImage.load(url) {
        supportStartPostponedEnterTransition()
    }
}
複製代碼

GlideLoader.kt

fun ImageView.load(url: String, onLoadingFinished: () -> Unit = {}) {
    val listener = object : RequestListener<Drawable> {
        override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
            onLoadingFinished()
            return false
        }

        override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
            onLoadingFinished()
            return false
        }
    }
    Glide.with(this)
            .load(url)
            .apply(RequestOptions.placeholderOf(R.drawable.placeholder))
            .listener(listener)
            .into(this)
}
複製代碼

如今咱們在過渡前圖片已經加載了,但能夠看到的是在進入過渡以前以及退出過渡以後會有奇怪的毛刺,咱們將在下一步處理它。

第三步: 禁止轉換

之因此有這個毛刺效果是因爲Glide在加載時進行的優化。 默認的, Glide爲了匹配目標view會調整圖片大小並裁剪圖片。可是Android過渡框架在過渡開始時會從目標視圖獲取圖像,而且將它轉換到源視圖的圖像。咱們可讓Glide不進行這些優化:
GlideLoader.kt

fun ImageView.load(url: String, onLoadingFinished: () -> Unit = {}) {
    ...
    val requestOptions = RequestOptions.placeholderOf(R.drawable.placeholder)
            .dontTransform()
    Glide.with(this)
            .load(url)
            .apply(requestOptions)
            .listener(listener)
            .into(this)
}
複製代碼

咱們也能夠在Glide中使用原始圖片大小。 這會減小過渡延遲,由於原始圖片會存在於內存中,不會在磁盤緩存中。注意:此操做會讓你的的程序變慢,須要當心使用。若是你必定要這樣作的花,這裏有個修改的分支能夠參考下.

如今能夠正常使用了,但在一些錯誤連接圖片獲取未加載完成但圖片就會有一個小問題。

第四步: 僅在Cache時傳輸

咱們要在任何條件下均可以無縫過渡,最簡單的方式是從緩存中獲取圖片 (或者若是圖片未加載完成時, 就使用佔位圖)。 DetailActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    detailImage.load(url, loadOnlyFromCache = true) {
        supportStartPostponedEnterTransition()
    }
}
複製代碼

GlideLoader.kt

fun ImageView.load(url: String, loadOnlyFromCache: Boolean = false, onLoadingFinished: () -> Unit = {}) {
    ...
    val requestOptions = RequestOptions.placeholderOf(R.drawable.placeholder)
            .dontTransform()
            .onlyRetrieveFromCache(loadOnlyFromCache)
    ...
}
複製代碼

固然,這意味着用戶打開詳情頁面時,在圖片加載完成以前,只會顯示佔位圖。它會在過渡結束後經過第二次請求來修復:
DetailActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    window.sharedElementEnterTransition = TransitionSet()
            .addTransition(ChangeImageTransform())
            .addTransition(ChangeBounds())
            .apply {
                doOnEnd { detailImage.load(url) }
            }
}	
複製代碼

最終,咱們有了一個很是棒的過渡轉場效果,它能夠工做在各類條件下。您能夠在GitHub上查看整個示例,也能夠在Toast App中查看更多示例。

總結

共享元素過渡提供了視覺上的連續性,並保持了用戶的注意力。 可是咱們應該記住,咱們的互聯網鏈接可能不好,凍結的過渡可能會激怒用戶。 我相信這4個步驟將在任何狀況下幫助您使您的應用程序美觀且快速。

相關連接

Workcation App – Part 4. Shared Element Transition with RecyclerView and Scenes
Meaningful Motion with Shared Element Transition and Circular Reveal Animation
Workcation App – Part 1. Fragment custom transition
How to Learn Android Development Programming – 6 Steps for Beginners
6 Misconceptions about TDD – Part 4. There is one right granularity of steps

相關文章
相關標籤/搜索