Android使用動(dòng)畫進(jìn)行Fragment、Activity的切換

我們知道有些Fragment、Activity的跳轉(zhuǎn)花時(shí)間比較長(zhǎng)。這時(shí)候通過給跳轉(zhuǎn)加上一些動(dòng)畫效果可以讓用戶感受不到這樣等待,給用戶很好的應(yīng)用體驗(yàn)。個(gè)人覺得PayPal的動(dòng)畫效果就做得蠻好的。

給Fragment的轉(zhuǎn)換添加動(dòng)畫

這里就難得想例子了。直接吧官方給的例子修改的一下進(jìn)行講解(官方的例子是一個(gè)用翻轉(zhuǎn)動(dòng)畫實(shí)現(xiàn)Fragment切切換的例子):

先定義好四個(gè)Animator。分別是:

  • enter_animator: 這個(gè)動(dòng)畫將作用于將被添加的Fragment中的View
  • exit_animator: 這個(gè)動(dòng)畫作用于將會(huì)被移除的Fragemnt中的View
  • pop_enter_animator: 這個(gè)動(dòng)畫作用于FragmentManager#popBackStack()方法調(diào)用或者back鍵按后,將被添加的Fragment中的View
  • pop_exit_animator: 這個(gè)動(dòng)畫作用于FragmentManager#popBackStack()方法調(diào)用或者back鍵按后,將被移除的Fragment中的View

enter_animtor.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="0"
        android:propertyName="alpha"
        android:valueFrom="1.0"
        android:valueTo="0.0" />
    
    <objectAnimator
        android:duration="@integer/card_flip_full_time"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:propertyName="rotationY"
        android:valueFrom="180"
        android:valueTo="0" />

    <objectAnimator
        android:duration="0"
        android:propertyName="alpha"
        android:startOffset="@integer/card_flip_half_time"
        android:valueFrom="0.0"
        android:valueTo="1.0" />
</set>

這里定義了一個(gè)屬性AnimatorSet,這個(gè)set中有三個(gè)ObjectAnimator。

第一個(gè)是在開始的時(shí)候,就將將要出現(xiàn)的Fragment中的View的透明度設(shè)置為透明。

第二個(gè)是對(duì)將要出現(xiàn)的Fragment中的View進(jìn)行rotationY的變換。

第三個(gè)是在動(dòng)畫轉(zhuǎn)換到一半的時(shí)候?qū)⒁霈F(xiàn)的Fragment中的View的透明設(shè)置為完全不透明

exit_animator.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="@integer/card_flip_full_time"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:propertyName="rotationY"
        android:valueFrom="0"
        android:valueTo="-180" />

    <objectAnimator
        android:duration="1"
        android:propertyName="alpha"
        android:startOffset="@integer/card_flip_half_time"
        android:valueFrom="1.0"
        android:valueTo="0.0" />
</set>

其中,第一個(gè)ObjectAnimator是對(duì)將要消失的Fragment中的View進(jìn)行rotaionY的變換。

第二個(gè)是在動(dòng)畫執(zhí)行到一半的時(shí)候?qū)⒁南У腇ragment中的View透明度轉(zhuǎn)換為透明。

后面兩個(gè)動(dòng)畫,我就不再進(jìn)行詳細(xì)的介紹了。相當(dāng)于前面這兩個(gè)動(dòng)畫的逆過程。

pop_enter_animator.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="1"
        android:propertyName="alpha"
        android:valueFrom="1.0"
        android:valueTo="0.0" />

    <objectAnimator
        android:duration="@integer/card_flip_full_time"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:propertyName="rotationY"
        android:valueFrom="-180"
        android:valueTo="0" />

    <objectAnimator
        android:duration="1"
        android:propertyName="alpha"
        android:startOffset="@integer/card_flip_half_time"
        android:valueFrom="0.0"
        android:valueTo="1.0" />
</set>

pop_exit_animator.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="@integer/card_flip_full_time"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:propertyName="rotationY"
        android:valueFrom="0"
        android:valueTo="180" />

    <objectAnimator
        android:duration="1"
        android:propertyName="alpha"
        android:startOffset="@integer/card_flip_half_time"
        android:valueFrom="1.0"
        android:valueTo="0.0" />
</set>

下面是Activity的xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".animation.CardFlipActivity">

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="fragment flip"
        android:textAllCaps="false" />

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </FrameLayout>

</LinearLayout>

Activity的代碼:

class CardFlipActivity : AppCompatActivity() {

    //是否現(xiàn)在背面(也就是第二個(gè)Fragment)
    private var mShowingBack = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_card_flip)

        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                    .add(R.id.container, CardFrontFragment())
                    .commit()
        }
        
        btn.setOnClickListener {
            flip()
        }
    }

    private fun flip() {
        if (mShowingBack) {
            supportFragmentManager.popBackStack()
            mShowingBack = false
            return
        }
        mShowingBack = true
        supportFragmentManager
                .beginTransaction()
                //為Fragment轉(zhuǎn)換設(shè)置動(dòng)畫
                .setCustomAnimations(R.animator.enter_animator,
                        R.animator.exit_animator,
                        R.animator.pop_enter_animator,
                        R.animator.pop_exit_animator)
                .replace(R.id.container, CardBackFragment())
                .addToBackStack(null)
                .commit()
    }

    override fun onBackPressed() {
        mShowingBack = false
        super.onBackPressed()
    }

    /**
     * 正面Fragment(第一個(gè)Fragment)
     */
    class CardFrontFragment : Fragment() {
        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 
                                  savedInstanceState: Bundle?): View? {
            return inflater.inflate(R.layout.fragment_card_front, container, false)
        }
    }

    /**
     * 背面Fragment(第二個(gè)Fragment)
     */
    class CardBackFragment : Fragment() {
        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 
                                  savedInstanceState: Bundle?): View? {
            return inflater.inflate(R.layout.fragment_card_back, container, false)
        }
    }
}

很簡(jiǎn)單這里我就不詳細(xì)講了。

重點(diǎn)就是flip方法中為Fragment切換設(shè)定動(dòng)畫的方法。那個(gè)四個(gè)參數(shù)可以分別對(duì)應(yīng)動(dòng)畫去看。然后不太清楚可以直接跳進(jìn)行看。之前在四個(gè)動(dòng)畫的介紹中已經(jīng)說了,這里我就不太想再說了。

沒錯(cuò)為Fragment設(shè)置動(dòng)畫就是這么easy!!!

給Activity的轉(zhuǎn)換添加過渡動(dòng)畫

為Activity設(shè)置動(dòng)畫仍然很簡(jiǎn)單。

為Activity將借助于Transition來進(jìn)行實(shí)現(xiàn)。前面已經(jīng)講了Transition了。這里就直接通過例子來進(jìn)行講解了。

定義Activity過渡動(dòng)畫

定義Activity的過渡動(dòng)畫仍然可以在xml和代碼中進(jìn)行定義。

在styles.xml定義的過渡動(dòng)畫會(huì)對(duì)所有的Activity使用。在代碼中進(jìn)行定義的過渡動(dòng)畫只會(huì)對(duì)被定義的Activity使用

下面是在styles.xml中定義的例子:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>

        <!--這個(gè)屬性用于啟用Activity過渡動(dòng)畫-->
        <item name="android:windowActivityTransitions">true</item>

        <!--下面兩個(gè)屬性設(shè)定Activity進(jìn)入、離開的過渡動(dòng)畫-->
        <item name="android:windowEnterTransition">@android:transition/explode</item>
        <item name="android:windowExitTransition">@android:transition/explode</item>

        <!--下面兩個(gè)屬性定義Activity進(jìn)入、離開時(shí)共享的控件的轉(zhuǎn)移動(dòng)畫-->
        <item name="android:windowSharedElementEnterTransition">@transition/change_image_transform
        </item>
        <item name="android:windowSharedElementExitTransition">@transition/change_image_transform
        </item>
    </style>
</resources>

其中給android:windowEnterTransition、android:windowExitTransition這個(gè)兩個(gè)動(dòng)畫需要繼承自Visibility才行。比如說:Explode、Slide、Fade

下面是在代碼中對(duì)應(yīng)的定義:

with(window){
    requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)

    enterTransition = Slide(Gravity.BOTTOM)
    exitTransition = Slide(Gravity.TOP)

    sharedElementEnterTransition = 
    TransitionInflater.from(this@FirstAnimationActivity)
            .inflateTransition(R.transition.change_image_transform)
    sharedElementExitTransition = 
    TransitionInflater.from(this@FirstAnimationActivity)
            .inflateTransition(R.transition.change_image_transform)
}

其中change_image_transform.xml定義如下:

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

這個(gè)很簡(jiǎn)單就不介紹了。

使用指定的Transition來啟動(dòng)不帶共享View的Activity

下面是FirstAnimationActivity的xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".animation.FirstAnimationActivity">

    <Button
        android:id="@+id/btn"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="animate to second"
        android:textAllCaps="false"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/iv"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="centerCrop"
        android:src="@drawable/my_animstate_drawable"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/btn" />

</androidx.constraintlayout.widget.ConstraintLayout>

通過定義好的Transition來啟動(dòng)Activity就只需要想下面一樣進(jìn)行定義:

class FirstAnimationActivity : AppCompatActivity() {

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_first_animation)

        btn.setOnClickListener{
            startActivity(Intent(this, SecondAnimationActivity::class.java),
                    ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle())
        }
    }
}

是不是很簡(jiǎn)單就是加上ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle()作為startActivity的參數(shù)就行了。

使用Transition來啟動(dòng)帶有單個(gè)共享View的Activity

這個(gè)也很簡(jiǎn)單,這里講以共享一個(gè)ImageView的來作為例子。

定義共享View的方式有兩種:

  • 在xml中通過transitionName來進(jìn)行定義。
  • 在代碼中通過ViewCompat.setTransitionName()來動(dòng)態(tài)進(jìn)行設(shè)置

一般如果View是在xml中進(jìn)行定義的就用第一種方式,如果View是在代碼中進(jìn)行定義的就用第二種。

在xml中定義

下面是FirstAnimationActivity中修改的代碼(其中xml仍然是上面的xml)

class FirstAnimationActivity : AppCompatActivity() {

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onCreate(savedInstanceState: Bundle?) {
...
        btn.setOnClickListener{
            startActivity(Intent(this, SecondAnimationActivity::class.java),
                    ActivityOptionsCompat.makeSceneTransitionAnimation(this,
                            iv, "iv").toBundle())
        }
    }
}

這個(gè)給ActivityOptionsCompat的makeSceneTransitionAnimation加了兩個(gè)參數(shù),第一個(gè)是要當(dāng)前l(fā)ayout中要共享的View,第二個(gè)是在另外的Activity中對(duì)應(yīng)的View的transitionName。

下面是SecondAnimationActivity中的xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/cl"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".animation.SecondAnimationActivity">

    <TextView
        android:id="@+id/tv"
        style="?android:textAppearanceMedium"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#a6c"
        android:gravity="center"
        android:text="Hello, this is activity animation"
        app:layout_constraintBottom_toTopOf="@id/iv"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/iv"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="center"
        android:src="@drawable/my_animstate_drawable"
        android:transitionName="iv"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv" />

</androidx.constraintlayout.widget.ConstraintLayout>

這里需要注意的有一個(gè)地方就是ImageView中的transitionName,這個(gè)就是前面makeSceneTransitionAnimation的新增的第二個(gè)參數(shù)。

在代碼中定義

在代碼中定義一般是如果在代碼中動(dòng)態(tài)生成View的情況下進(jìn)行使用的。這里就不在代碼中生成了。直接使用xml中的ImageView進(jìn)行舉例。

下面是SecondAnimationActivity的代碼:

class SecondAnimationActivity : AppCompatActivity() {
    
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_second_animation)
        ViewCompat.setTransitionName(iv, "iv")
    }
}

像這樣定義后,你就可以將xml中ImageView中的transitionName屬性去掉了?。?!

使用Transition來啟動(dòng)帶有多個(gè)共享View的Activity

當(dāng)我們有多個(gè)View需要在兩個(gè)View中進(jìn)行共享的時(shí)候該怎么辦?其實(shí)很簡(jiǎn)單基本和單個(gè)的情況是一樣的。

只有下面的的一點(diǎn)不同:

下面通過代碼舉個(gè)例子,具體的例子我就寫了:

startActivity(Intent(this, SecondAnimationActivity::class.java),
        ActivityOptionsCompat.makeSceneTransitionAnimation(this,
        Pair(view1, "view1"),
        Pair(view2, "view2")...).toBundle())

總結(jié)

這篇博客主要就是兩個(gè)內(nèi)容。

  • 一個(gè)是給Fragment的跳轉(zhuǎn)設(shè)置動(dòng)畫,通過setCustomAnimations進(jìn)行設(shè)置
  • 然后就是為Activity設(shè)定跳轉(zhuǎn)的過渡動(dòng)畫。這個(gè)重要的就是ActivityOptionsCompat的makeSceneTransitionAnimation靜態(tài)方法的使用
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 情緒真的是匹難以駕馭的野馬,控制不好很壞事,害人害己。 心情不好的時(shí)候,什么事都做不了,會(huì)消極、會(huì)崩潰、會(huì)遷怒。 ...
    游走星宿閱讀 1,773評(píng)論 2 4
  • 很多時(shí)候,我們很強(qiáng)調(diào)原創(chuàng)內(nèi)容但并不等于網(wǎng)站就一定要原創(chuàng)內(nèi)容,SEO就一定要原創(chuàng)內(nèi)容才能培養(yǎng)起來。并不是這樣。轉(zhuǎn)載內(nèi)...
    老朱seo閱讀 286評(píng)論 0 0
  • 在一座公園外,住著一位老奶奶,她的孫女病了,她很焦急。她聽人家說,用蒲公英泡水喝,能治療小孫女的病,就每天上午都到...
    佘璽閱讀 2,034評(píng)論 9 14
  • 6月30日(周五)兩市低開走高,再收小陽線,中小創(chuàng)同步震蕩,創(chuàng)業(yè)板再漲0.25%。從盤面上看,次新股、在線...
    亻壬我行閱讀 140評(píng)論 1 10

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