炫酷的Android過(guò)渡動(dòng)畫(huà)

【桃花潭水深千尺,不及汪倫送我情】

不知道大家有沒(méi)有發(fā)現(xiàn),Android版的掘金有下面這個(gè)小小動(dòng)畫(huà):點(diǎn)擊作者頭像跳轉(zhuǎn)到作者的詳情頁(yè),而作者頭像會(huì)從當(dāng)前界面通過(guò)動(dòng)畫(huà)過(guò)渡詳情頁(yè)界面。

image

知識(shí)貧乏限制了我的視野,真心想不到這怎么實(shí)現(xiàn)的?

最近在寫(xiě)動(dòng)畫(huà)方面文章時(shí)候,從網(wǎng)上找到了答案:Activity過(guò)渡動(dòng)畫(huà)中的共享元素過(guò)渡。

本文的初衷,是和大家一起掃盲,如果對(duì)你有用,歡迎點(diǎn)贊,讓更多的小伙伴多學(xué)點(diǎn)知識(shí)。小小的動(dòng)畫(huà),隱藏著巨大的知識(shí)點(diǎn);怪不得面試造火箭,工作擰螺絲,這是知識(shí)儲(chǔ)備,雖然可能一輩子也用不上。

系列好文推薦

Android屬性動(dòng)畫(huà),看完這篇夠用了吧

Android矢量圖動(dòng)畫(huà):每人送一輛掘金牌小黃車(chē)

一、Activity切換過(guò)渡動(dòng)畫(huà)

Activity過(guò)渡動(dòng)畫(huà)包含進(jìn)入過(guò)渡退出過(guò)渡共享元素過(guò)渡三個(gè)動(dòng)畫(huà),它們同樣僅支持Android 5.0+版本。

一)、共享元素過(guò)渡動(dòng)畫(huà)

共享元素過(guò)渡指的兩個(gè)Activity共享的視圖如何在兩個(gè)Activity之間進(jìn)行過(guò)渡。例如上面的Gif圖,共享視圖就是ImageView。

共享元素也分一個(gè)元素和多個(gè)元素。

定義共享元素過(guò)渡效果步驟如下:

  1. 在兩個(gè)Activity定義兩個(gè)相同類(lèi)型的View;
  2. 給兩個(gè)View設(shè)置相同的transitionName屬性;
  3. 通過(guò)ActivityOptions.makeSceneTransitionAnimation()函數(shù)生成Bundle對(duì)象;
  4. startActivity()函數(shù)傳遞bundle對(duì)象。

栗子講解,清晰易懂:

  1. 分別在activity_first.xmlactivity_second.xml布局文件定義ImageView組件,并將transitionName屬性設(shè)為activityTransform
<!--activity_first.xml文件內(nèi)容-->

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/ivImage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_one"
        android:transitionName="activityTransform" />

    <TextView
        android:id="@+id/tvText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:gravity="center"
        android:text="我是第一個(gè)Activity"
        android:textColor="@color/c_333"
        android:textSize="18sp" />
</LinearLayout>

<!--activity_second.xml文件內(nèi)容-->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/ivImage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:adjustViewBounds="true"
        android:src="@mipmap/ic_one"
        android:transitionName="activityTransform" />

    <TextView
        android:id="@+id/tvText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/ivImage"
        android:layout_marginBottom="10dp"
        android:gravity="center"
        android:text="我是第2個(gè)Activity"
        android:textColor="@color/c_333"
        android:textSize="18sp" />
</RelativeLayout>

預(yù)覽圖

image

activityTransform屬性也可以通過(guò)代碼設(shè)置。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    ivImage.transitionName="activityTransform"
}
  1. FirstActivity中給ImageView設(shè)置點(diǎn)擊事件,跳轉(zhuǎn)到第二個(gè)Activity。
ivImage.setOnClickListener {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//判斷Android版本
        val bundle =
            ActivityOptions.makeSceneTransitionAnimation(this, ivImage, "activityTransform")
                .toBundle()
        startActivity(Intent(this, SecondActivity::class.java), bundle)
    } else {
        startActivity(Intent(this, SecondActivity::class.java))
    }
}

代碼中,先判斷當(dāng)前Android版本是否大于等于5.0,大于或等于Android 5.0的話就設(shè)置共享元素動(dòng)畫(huà),小于5.0 就正常啟動(dòng)第二個(gè)Activity

通過(guò)ActivityOptions.makeSceneTransitionAnimation()創(chuàng)建啟動(dòng)Activity過(guò)渡的一些參數(shù),makeSceneTransitionAnimation()函數(shù)第一個(gè)參數(shù)為Activity對(duì)象;第二個(gè)參數(shù)為共享元素組件,這里設(shè)置為idivImageImageView視圖;第三個(gè)參數(shù)為transitionName屬性的值,這里是activityTransform。在調(diào)用AcivityOptions對(duì)象toBundle函數(shù),包裝成Bundle對(duì)象。

效果圖:

image

多個(gè)共享元素過(guò)渡

多個(gè)共享元素過(guò)渡也很簡(jiǎn)單,只需要調(diào)用makeSceneTransitionAnimation()函數(shù)的另外一個(gè)重載函數(shù)即可。

  1. 在前面XML布局的基礎(chǔ)上,給TextView增加transitionName屬性:textTransform。
<!--activity_first.xml文件內(nèi)容-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/ivImage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_one"
        android:transitionName="activityTransform" />

    <TextView
        android:id="@+id/tvText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:gravity="center"
        android:transitionName="textTransform"
        android:text="我是第一個(gè)Activity"
        android:textColor="@color/c_333"
        android:textSize="18sp" />
</LinearLayout>

<!--activity_second.xml文件內(nèi)容-->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/ivSecondImage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:adjustViewBounds="true"
        android:src="@mipmap/ic_one"
        android:transitionName="activityTransform" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:transitionName="textTransform"
        android:layout_above="@id/ivSecondImage"
        android:layout_marginBottom="10dp"
        android:gravity="center"
        android:text="我是第2個(gè)Activity"
        android:textColor="@color/c_333"
        android:textSize="18sp" />
</RelativeLayout>
  1. 構(gòu)建多個(gè)Pair對(duì)象,并傳遞給makeSceneTransitionAnimation()函數(shù),啟動(dòng)Activity。
ivImage.setOnClickListener {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {             
    
    val imagePair=Pair<View,String>(ivImage,"activityTransform")
    val textPair=Pair<View,String>(ivImage,"textTransform")
    
    val bundle =
        ActivityOptions.makeSceneTransitionAnimation(this,
                imagePair,textPair).toBundle()
        
        startActivity(Intent(this, SecondActivity::class.java), bundle) 
    } else {
        startActivity(Intent(this, SecondActivity::class.java))
    }
}

這里主要是通過(guò)將共享視圖和transitionName屬性的值包裝到Pair對(duì)象,其他操作和一個(gè)共享元素的操作步驟并無(wú)區(qū)別。

效果圖:

image

深坑提醒

有時(shí)從RecyclerView界面進(jìn)入到詳情頁(yè),由于詳情頁(yè)加載延遲,可能出現(xiàn)沒(méi)有效果。例如ImageView從網(wǎng)絡(luò)加載圖片,可能A界面到B界面沒(méi)效果,B回到A界面有效果。

image

解決步驟:

  1. setContentView后添加下面代碼,延遲加載過(guò)渡動(dòng)畫(huà)。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    postponeEnterTransition()
}
  1. 在共享元素視圖加載完畢,或者圖片加載完畢后調(diào)用下面代碼,開(kāi)始加載過(guò)渡動(dòng)畫(huà)。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    startPostponedEnterTransition()
}
image

例如我是在Glide加載完再調(diào)用:

 Glide.with(mContext)
                    .asBitmap()
                    .load(value?.avatar ?: "")
                    .listener(object : RequestListener<Bitmap> {
                        override fun onResourceReady(resource: Bitmap?, model: Any?, target: Target<Bitmap>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
                            animatorCallback?.invoke()//回調(diào)開(kāi)始加載過(guò)渡動(dòng)畫(huà)
                            return false
                        }

                        override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Bitmap>?, isFirstResource: Boolean): Boolean {
                            animatorCallback?.invoke()//回調(diào)開(kāi)始加載過(guò)渡動(dòng)畫(huà)
                            return false
                        }
                    })
                    .apply(RequestOptions.circleCropTransform())
                    .placeholder(R.mipmap.ic_default)
                    .error(R.mipmap.ic_default)
                    .into(authorBinding!!.ivAvatar)

大家也可以考慮下面代碼:

shareElement.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
                override fun onPreDraw(): Boolean {
                    shareElement!!.viewTreeObserver.removeOnPreDrawListener(this)
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        animatorCallback?.invoke()
                    }
                    return true
                }
            })

二)、Activity進(jìn)入過(guò)渡與退出過(guò)渡動(dòng)畫(huà)

與共享元素相反的,就是Activity進(jìn)入與退出過(guò)渡動(dòng)畫(huà),兩個(gè)Activity之間在沒(méi)有共享的視圖情況下進(jìn)行動(dòng)畫(huà)切換。下面先看三種動(dòng)畫(huà)效果圖:爆炸式效果淡入淡出式效果滑動(dòng)式效果

  • 爆炸式:將視圖移入場(chǎng)景中心或從中移出;
  • 滑動(dòng)式:將視圖從場(chǎng)景的其中一個(gè)邊緣移入或移出;
  • 爆炸式:通過(guò)更改視圖的不透明度,在場(chǎng)景中添加視圖或從中移除視圖;

第一個(gè)界面采用Fade淡入淡出效果,第二個(gè)界面采用了Explode爆炸效果。

image

前后兩個(gè)界面都采用了Slide滑入滑出效果。

image

利用Android現(xiàn)有的過(guò)渡框架,實(shí)現(xiàn)起來(lái)是很簡(jiǎn)單的,步驟如下:

  1. ActivityonCreate()方法中調(diào)用 setContentView()前設(shè)置啟用窗口過(guò)渡屬性;
window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
  1. 創(chuàng)建過(guò)渡效果對(duì)象SlideExplode、Fade;
val slide=Slide()
slide.slideEdge=Gravity.START
slide.duration=300//效果時(shí)長(zhǎng),一般Activity切換時(shí)間很短,不建議設(shè)置過(guò)長(zhǎng)

如果是Slide效果,可以設(shè)置slideEdge屬性來(lái)指定滑動(dòng)方向,默認(rèn)是Gravity.BOTTOM

  1. 將過(guò)渡效果設(shè)置給window相關(guān)屬性,設(shè)置;
//退出當(dāng)前界面的過(guò)渡動(dòng)畫(huà)
window.exitTransition = slide
//進(jìn)入當(dāng)前界面的過(guò)渡動(dòng)畫(huà)
window.enterTransition = slide
//重新進(jìn)入界面的過(guò)渡動(dòng)畫(huà)
window.reenterTransition = slide
  1. 調(diào)用第二個(gè)Activity界面,使用過(guò)渡效果。
 startActivity(
        Intent(this, SecondActivity::class.java),
        ActivityOptions.makeSceneTransitionAnimation(this).toBundle())

那么ActivityOnCreate()方法看起來(lái)是這樣子的。

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
            window.allowEnterTransitionOverlap=false
            Slide().apply {
                duration = 300
                excludeTarget(android.R.id.statusBarBackground, true)
                excludeTarget(android.R.id.navigationBarBackground, true)
            }.also {
                window.exitTransition = it
                window.enterTransition = it
                window.reenterTransition = it
            }
        }
        
        setContentView(R.layout.activity_first)

        ivContent.setOnClickListener {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                startActivity(
                    Intent(this, SecondActivity::class.java),
                    ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
                )
            }
        }
    }

上面代碼中調(diào)用 了excludeTarget()方法將狀態(tài)欄和導(dǎo)航欄排除在過(guò)渡動(dòng)畫(huà)效果之外。否則會(huì)跟著一起起動(dòng)畫(huà)效果,不是很美觀。

正常情況,退出與進(jìn)入過(guò)渡動(dòng)畫(huà)會(huì)有一小段交叉的過(guò)程,而window.allowEnterTransitionOverlap=false就是禁止交叉,只有退出過(guò)渡動(dòng)畫(huà)結(jié)束后才會(huì)再顯示進(jìn)入過(guò)渡動(dòng)畫(huà)。

如果第二個(gè)Activityfinish掉后,回到第一個(gè)Activity界面也想有過(guò)渡效果,就不要手動(dòng)調(diào)用finish(),可以調(diào)用finishAfterTransition ()方法。

三)、兼容Android 5.0前

如果Android 5.0前也想要有切換動(dòng)畫(huà)怎么辦?

  1. res/anim文件夾下創(chuàng)建想要的效果:
<alpha 
    xmlns:android="http://schemas.android.com/apk/res/android"
        android:interpolator="@interpolator/decelerate_quad"
        android:fromAlpha="0.0"
        android:toAlpha="1.0"
        android:duration="@android:integer/config_longAnimTime" />
  1. 在啟動(dòng)Activity后調(diào)用overridePendingTransition()方法。
val intent = Intent(this, TestActivity2::class.java)
startActivity(intent)
overridePendingTransition(R.anim.fade_in, R.anim.fade_out)

overridePendingTransition()方法第一個(gè)參數(shù)為下一個(gè)界面進(jìn)入動(dòng)畫(huà),第二個(gè)參數(shù)為當(dāng)前界面退出動(dòng)畫(huà)。

到這里,Activity的切換使用過(guò)渡動(dòng)畫(huà)基本就結(jié)束了。有朋友可能會(huì)問(wèn),只有Activity切換才能應(yīng)用過(guò)渡效果么?

二、布局變化過(guò)渡效果

在上一節(jié)要理解一個(gè)概念:場(chǎng)景。布局的顯示與隱藏可以理解分別為一個(gè)場(chǎng)景,過(guò)渡動(dòng)畫(huà)就是解決場(chǎng)景切換帶來(lái)的生硬視覺(jué)感受。Activity切換過(guò)渡動(dòng)畫(huà)指在兩個(gè)Activity之間,而布局變化過(guò)渡動(dòng)畫(huà),是指同個(gè)Activity之間View的變化過(guò)渡動(dòng)畫(huà)。

一)、手動(dòng)創(chuàng)建Scene

手動(dòng)創(chuàng)建場(chǎng)景的話,需要我們自己創(chuàng)建起始和結(jié)束場(chǎng)景,利用現(xiàn)有的過(guò)渡效果來(lái)達(dá)到兩個(gè)場(chǎng)景的切換。默認(rèn)情況下,當(dāng)前界面就是起始場(chǎng)景。

  1. 創(chuàng)建起始場(chǎng)景和結(jié)束場(chǎng)景的xml布局。起始場(chǎng)景和結(jié)束場(chǎng)景需要有相同根元素,例如下面代碼idflConatentFrameLayout布局。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tvText"
        android:text="內(nèi)容過(guò)渡動(dòng)畫(huà)"
        android:gravity="center"
        android:textSize="18sp"
        android:layout_width="match_parent"
        android:layout_height="50dp"/>

    <FrameLayout
        android:id="@+id/flContent"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="0dp">
      <include layout="@layout/layout_first_scene"/>
    </FrameLayout>

</LinearLayout>

初始視圖,第一個(gè)場(chǎng)景,布局layout_first_scene.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:id="@+id/tvFirst"
        android:textSize="18sp"
        android:layout_marginTop="100dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal|top"
        android:text="感謝大家閱讀文章" />
</LinearLayout>

第二個(gè)場(chǎng)景,布局layout_second_scene.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:textSize="18sp"
        android:layout_marginTop="100dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal|top"
        android:text="我是新小夢(mèng)\n歡迎大家點(diǎn)贊支持一下" />
</LinearLayout>
  1. 創(chuàng)建起始場(chǎng)景和結(jié)束場(chǎng)景。
val firstScene = Scene.getSceneForLayout(flContent, R.layout.layout_first_scene, this)
val secondScene = Scene.getSceneForLayout(flContent, R.layout.layout_sencod_scene, this)

默認(rèn)情況下,過(guò)渡動(dòng)畫(huà)應(yīng)用整個(gè)場(chǎng)景,如果場(chǎng)景某個(gè)View不參加,可以通過(guò)過(guò)渡效果對(duì)象removeTarget()方法進(jìn)行移除。

Slide(Gravity.TOP).removeTarget(tvNoJoin)
  1. 點(diǎn)擊時(shí),進(jìn)行場(chǎng)景過(guò)渡。
tvText.setOnClickListener {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        if (isFirst) {
            TransitionManager.go(secondScene, Slide(Gravity.TOP))
        }else{
            TransitionManager.go(firstScene, Slide(Gravity.TOP))
        }
        isFirst=!isFirst
    }
}

TransitionManager.go()第一個(gè)參數(shù)表示結(jié)束場(chǎng)景,第二個(gè)參數(shù)表示當(dāng)前場(chǎng)景退出時(shí)過(guò)渡效果,當(dāng)前場(chǎng)景就是初始場(chǎng)景。

效果圖:

image

二)、系統(tǒng)自動(dòng)創(chuàng)建Scene

這種情況,我們調(diào)用TransitionManager.beginDelayedTransition(sceneRoot)函數(shù)時(shí),系統(tǒng)會(huì)自動(dòng)記錄當(dāng)前sceneRoot節(jié)點(diǎn)下所有要進(jìn)行動(dòng)畫(huà)的視圖作為起始節(jié)點(diǎn),下一幀中再次記錄sceneRoot子節(jié)點(diǎn)下所有 起始場(chǎng)景進(jìn)行動(dòng)畫(huà)狀態(tài)的視圖作為結(jié)束場(chǎng)景。這種一般用來(lái)改變視圖的屬性,然后進(jìn)行動(dòng)畫(huà)過(guò)渡,如View的寬高。

栗子

定義只有一個(gè)正方形的View,通過(guò)改變正方形的寬高為原來(lái)的2倍,來(lái)看看動(dòng)畫(huà)效果。

  1. activity_text.xml布局文件,定義idsceneRoot的根節(jié)點(diǎn),也是場(chǎng)景的根節(jié)點(diǎn)。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/sceneRoot"
    android:background="@color/colorPrimary">

    <View
        android:id="@+id/vSquare"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="50dp"
        android:background="@color/white" />
</RelativeLayout>
  1. TestActivityOnCreate方法中調(diào)用下面代碼,將正方形的寬高設(shè)置200dp。
vSquare.setOnClickListener {
   TransitionManager.beginDelayedTransition(sceneRoot)
   vSquare.layoutParams.apply {
       width = dp2px(200f, this@TestActivity)
       height = dp2px(200f, this@TestActivity)
   }.also {
       vSquare.layoutParams = it
   }
}

效果圖:

image

三、過(guò)渡動(dòng)畫(huà)效果

上面的動(dòng)畫(huà)效果,都是采用系統(tǒng)內(nèi)置的,那具體有哪些動(dòng)畫(huà)效果,或支持自定義么?

過(guò)渡效果類(lèi)都繼承自Transition類(lèi),Transition類(lèi)持有場(chǎng)景切動(dòng)畫(huà)的相關(guān)信息,子類(lèi)的主要作用是捕獲屬性值(例如起始值和結(jié)束值)和如何演奏動(dòng)畫(huà)。從這里也可以看出,過(guò)渡動(dòng)畫(huà)也是屬性動(dòng)畫(huà)的一個(gè)擴(kuò)展與應(yīng)用。

一)、系統(tǒng)自帶過(guò)渡動(dòng)畫(huà)效果

系統(tǒng)支持將任何擴(kuò)展Visibility類(lèi)的過(guò)渡作為進(jìn)入或退出過(guò)渡,內(nèi)置繼承自Visibility的類(lèi)有ExplodeSlide、Fade;支持共享元素過(guò)渡的有:

  • changeScroll 為目標(biāo)視圖滑動(dòng)添加動(dòng)畫(huà)效果
  • changeBounds 為目標(biāo)視圖布局邊界的變化添加動(dòng)畫(huà)效果
  • changeClipBounds 為目標(biāo)視圖裁剪邊界的變化添加動(dòng)畫(huà)效果
  • changeTransform 為目標(biāo)視圖縮放和旋轉(zhuǎn)方面的變化添加動(dòng)畫(huà)效果
  • changeImageTransform 為目標(biāo)圖片尺寸和縮放方面的變化添加動(dòng)畫(huà)效果

代碼示例:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    TransitionSet().apply {
        addTransition(ChangeImageTransform())
        addTransition(ChangeBounds())
        addTransition(Fade(Fade.MODE_IN))
    }.also {
       window.sharedElementEnterTransition=it
    }
}

TransitionSet對(duì)象是動(dòng)畫(huà)的合集,可以將多個(gè)過(guò)渡效果組織起來(lái)。

也可以通過(guò)XML布局來(lái)實(shí)現(xiàn),在res/transition文件夾創(chuàng)建``:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:transitionOrdering="together">
    <changeImageTransform />
    <changeBounds />
    <fade />
</transitionSet>

transitionSetfade...等等的一些屬性和 Android矢量圖動(dòng)畫(huà):每人送一輛掘金牌小黃車(chē)文章 講到的一些屬性大同小異,這里不再?gòu)?fù)述。

代碼調(diào)用:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    TransitionInflater.from(this).inflateTransition(R.transition.transition_set).also {
        window.sharedElementEnterTransition=it
    }
}

效果圖:

image

當(dāng)現(xiàn)有的過(guò)渡效果不滿足日常需求時(shí),可以通過(guò)繼承Transition,定制自己的動(dòng)畫(huà)特效。

二)、自定義過(guò)渡動(dòng)畫(huà)

子類(lèi)繼承Transition類(lèi),并重寫(xiě)其三個(gè)方法。

class MyTransition : Transition() {
   override fun captureStartValues(transitionValues: TransitionValues?) {}

   override fun captureEndValues(transitionValues: TransitionValues?) {}

   override fun createAnimator(
       sceneRoot: ViewGroup?,
       startValues: TransitionValues?,
       endValues: TransitionValues?
   ): Animator {
       return super.createAnimator(sceneRoot, startValues, endValues)
   }
   
}

captureStartValues()captureEndValues()方法是必須實(shí)現(xiàn)的,捕獲動(dòng)畫(huà)的起始值和結(jié)束值,而createAnimator()方法,是用來(lái)創(chuàng)建自定義的動(dòng)畫(huà)。

參數(shù)TransitionValues可以理解是用來(lái)存儲(chǔ)View的一些屬性值,參數(shù)sceneRoot為根視圖。

自定義過(guò)渡效果感興趣可以參考:Android自定義Transition動(dòng)畫(huà)

好啦,過(guò)渡動(dòng)畫(huà)就講到這里~

參考文章:

官網(wǎng)文檔

酷炫的Activity切換動(dòng)畫(huà),打造更好的用戶體驗(yàn)

【碼字不易,點(diǎn)個(gè)贊,日后好查看】

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容