屬性動畫和Activity、Fragment過渡動畫等

主題是關於動畫的,可是不是什麼動畫的內容都包括。先泛泛的介紹一下,而後詳細的介紹一下翻代碼找見的一個好玩的動畫的使用。如下的內容包括Android 3和Android 3.1等引入的API,在使用中請注意版本。java

代碼都是用Kotlin寫的。若是你用的是新版的Android Studio。
建立項目以後,按下快捷鍵Shift+Ctrl+Alt+K就會自動把代碼從java轉換成Kotlin。
以後按照說明給項目配置Kotlin的插件便可。很簡單。Kotlin的官網在這裏:http://kotlinlang.org/。

Property Animation

Android 3.0引入了這個動畫。理論上能夠支持任意的對象(不限於View)在定義好的timer interval裏修改其屬性值,具體的介紹在這裏。Property Animation的引入很好的改進了以前的Tween Animation的不足。android

動畫和動畫監聽器

ObjectAnimator繼承自ValueAnimator, ValueAnimator繼承自Animator。屬性動畫通常使用ObjectAnimator來實現動畫。用ValueAnimator的話須要本身添加AnimatorUpdateListener來實現動畫中的每個time interval的增量值。dom

Animator說能夠添加AnimatorListener給他,因而咱們能夠給Animator和他的子類添加AnimatorListener。這個listener在動畫的不一樣階段都會有對應的回調方法。這些在上面提到的那篇文章中都有詳細的敘述,這裏再也不多說。ide

ViewPropertyAnimator

Android 3.1引入了ViewPropertyAnimator,用這個類能夠更加簡單的給View添加動畫。佈局

animate()方法返回了調用的view的ViewPropertyAnimator對象。這個對象能夠同時執行多個動畫。並且每一種動畫都提供了一組特定的方法。使用起來很是方便。好比:動畫

view.animate().translationX(100f).translationY(100f).scaleX(2.0f).scaleY(2.0f).withLayer()

這個動畫包括右移(translationX)、下移(translationY)和橫向放大兩倍(scaleX)、縱向放大兩倍(scaleY)。最後的withLayer()方法能夠用start()代替。只不過withLayer()會在狀況容許的狀況下調用硬件加速。ui

你也能夠定義一個Runnable在動畫開始和結束的時候執行。this

// 開始的時候
animLayout.animate().translationX(200f).withStartAction(object : Runnable {
    override fun run() {
        Toast.makeText(this@MainActivity, "Start Action", Toast.LENGTH_SHORT).show()
    }
})

動畫結束的時候執行的Runnable插件

animLayout.animate().alpha(0f).withEndAction {
    Toast.makeText(this@MainActivity, "End Action", Toast.LENGTH_SHORT).show()
}

以上用了Kotlin的兩種不一樣的寫法,可是意思都是同樣的,都是Runnable對象做爲參數傳入方法。第一個寫法:初始化一個Runnable接口的匿名對象。這個對象用object關鍵字代表。第二個寫法是,在Kotlin中只有一個方法的接口的實現,能夠直接把實現方法放在大括號裏扔給傳入的方法做爲參數。code

Kotlin是一個頗有意思的語言,其與java的互操做很是的方便。他並非一個運行在JVM上的特例獨行的語言,而是一個擁有腳本語言的便捷特色的Java。並且,這個便捷不是像Java 8那樣的妥協之後的產物。更多Kotlin的內容能夠看這裏

下面舉一個例子。這個例子就是讓一個背景爲藍色的view隔幾秒就作寬度(scaleX,scaleY)和位置(translationX,translationY)以及透明度(alpha)的變化。代碼:

fun executeAnim() {
    animView.scaleX = getScaleValue()
    animView.scaleY = getScaleValue()
    animView.translationX = getTransitionValue(animView.width, animView.scaleX)
    animView.translationY = getTransitionValue(animView.height, animView.scaleY)

    animView.animate()
            .scaleX(getScaleValue())
            .scaleY(getScaleValue())
            .translationX(getTransitionValue(animView.width, animView.scaleX))
            .translationY(getTransitionValue(animView.height, animView.scaleY))
            .setDuration(300).start()

    var animSet = AnimatorSet()
    animSet.duration = mFadeInOutMs
    animSet.playTogether(
            ObjectAnimator.ofFloat(animView, "alpha", 1.0f, 0.0f),
            ObjectAnimator.ofFloat(animView, "alpha", 0.0f, 1.0f)
    )
    animSet.start()
}

爲了更容易理解,給出getScaleValue(): FloatgetTransitionValue(value: Int, ratio: Float): Float的定義。這兩個方法都是產生隨機數的:

fun getScaleValue(): Float {
    return minScaleFactor + random.nextFloat() * (maxScaleFactor - minScaleFactor)
}

fun getTransitionValue(value: Int, ratio: Float): Float {
    return value * (ratio - 1.0f) * (random.nextFloat() - 0.5f)
}

如前所舒,上面的代碼就是用來讓一個view隔一段時間就執行一次動畫。這個動畫包括寬度、高度和上下的位移。爲了避免讓view的大小和位置變得沒譜,因此控制寬高和位置的隨機數值是控制在必定的比例範圍內的。

對比一下animView.animate().scaleX(getScaleValue()).scaleY(getScaleValue())...的多個動畫設置和下面的AnimatorSetalpha動畫設置,足見其方便程度。

佈局動畫(Layout animation)

使用LayoutTransition類,能夠實如今佈局容器上添加一個動畫,而且在這個容器內的view樹的變化都會以動畫展示出來。

這部分直接上代碼了,各位跑起來就能夠看到運行的結果。點擊按鈕,添加一個view,在添加的過程會有一個alpha動畫。

佈局是這樣的(刪去了部分主題無關的內容):

<RelativeLayout >

    <Button
        android:id="@+id/add_view_button"
        android:text="ADD View" />

    <LinearLayout
        android:id="@+id/layout_container"
        android:background="@android:color/holo_blue_dark"
        android:orientation="vertical">

    </LinearLayout>
</RelativeLayout>

Kotlin的代碼「

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

    val layoutContainer = findViewById(R.id.layout_container) as LinearLayout

    // Layout transition animation
    val layoutTransition = LayoutTransition()
    layoutTransition.enableTransitionType(LayoutTransition.CHANGING)

    layoutContainer.layoutTransition = layoutTransition

    val addViewButton = findViewById(R.id.add_view_button) as Button
    addViewButton.setOnClickListener { v ->
        layoutContainer.addView(Button(this@LayoutTransitionDemoActivity))
    }
}

運行起來之後,點擊「Add View」按鈕使勁往上添加視圖,你會看到具體的效果。若是交互上沒有什麼特殊的要求,給ViewGroup添加一個LayoutTransition類動畫能夠有效的改善用戶體驗,並且也只須要簡單的幾行代碼就能夠完成。

Activity的過渡動畫

不只能夠給視圖添加動畫,也能夠給Activity添加過渡動畫。ActivityOptions類就是用來幹這個的。

val layoutTransAnimButton = findViewById(R.id.layout_trans_button) as Button
layoutTransAnimButton.setOnClickListener { v ->
    val i = Intent(this@MainActivity, LayoutTransitionDemoActivity::class.java)
    var activityOptions = ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.width, v.height)
    startActivity(i, activityOptions.toBundle())
}

只須要給startActivity()方法中添加一個ActivityOptions類的實例就能夠實現這個功能。並且不只於此,在這個類的API中還包括一些諸如makeThumbnailScaleUpAnimation(View source, Bitmap thumbnail, int startX, int startY)以及makeThumbnailScaleDownAnimation(View source, Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener)等這樣的方法。好比在瀑布流中,從一個item點擊以後跳轉到其餘Activity的動畫就能夠用這兩個方法來實現。

Fragment的過渡動畫

說完了Activity在說說Fragment的過渡動畫。這部分徹底能夠另外起一篇博文來細說一下。簡要歸納呢,設定動畫的代碼很容易,知識準備動畫內容多一點。下面的內容用到的Property Animation再也不是代碼實現,而是使用xml的方式實現。

首先咱們來討論一下這個過渡動畫應該是什麼樣子的。在Fragment進入的時候逐漸顯示出來(alpha從0到1),位置從100到0(translation x從100到0),在Fragment退出的時候正好相反。可是,進入和退出分兩個方向。或者須要咱們設置兩個方向的進入和退出。分別是從左到右和從右到左的。

下面開始實戰:在res目錄下建立一個animator目錄,專門用來存放上文提到的進入和退出動畫。若是你只想要知道具體的Fragment過渡動畫設置,能夠直接略過如下具體的動畫的設置部分。首先要穿件的是從左到右進入和退出的動畫:、
res/animator/fragment_slide_left_enter.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="@android:integer/config_mediumAnimTime"
        android:interpolator="@android:interpolator/decelerate_quint"
        android:propertyName="translationX"
        android:valueFrom="100dp"
        android:valueTo="0dp"
        android:valueType="floatType" />
    <objectAnimator
        android:duration="@android:integer/config_mediumAnimTime"
        android:interpolator="@android:interpolator/decelerate_quint"
        android:propertyName="alpha"
        android:valueFrom="0.0"
        android:valueTo="1.0"
        android:valueType="floatType" />
</set>

res/animator/fragment_slide_left_exit.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="@android:integer/config_mediumAnimTime"
        android:interpolator="@android:interpolator/decelerate_quint"
        android:propertyName="translationX"
        android:valueFrom="0dp"
        android:valueTo="-100dp"
        android:valueType="floatType" />
    <objectAnimator
        android:duration="@android:integer/config_mediumAnimTime"
        android:interpolator="@android:interpolator/decelerate_quint"
        android:propertyName="alpha"
        android:valueFrom="1.0"
        android:valueTo="0.0"
        android:valueType="floatType" />
</set>

從右到左的進入和退出:
res/animator/fragment_slide_right_enter.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="@android:integer/config_mediumAnimTime"
        android:interpolator="@android:interpolator/decelerate_quint"
        android:propertyName="translationX"
        android:valueFrom="-100"
        android:valueTo="0dp"
        android:valueType="floatType" />
    <objectAnimator
        android:duration="@android:integer/config_mediumAnimTime"
        android:interpolator="@android:interpolator/decelerate_quint"
        android:propertyName="alpha"
        android:valueFrom="0.0"
        android:valueTo="1.0"
        android:valueType="floatType" />
</set>

res/animator/fragment_slide_right_exit.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="@android:integer/config_mediumAnimTime"
        android:interpolator="@android:interpolator/decelerate_quint"
        android:propertyName="translationX"
        android:valueFrom="0dp"
        android:valueTo="100dp" />
    <objectAnimator
        android:duration="@android:integer/config_mediumAnimTime"
        android:interpolator="@android:interpolator/decelerate_quint"
        android:propertyName="alpha"
        android:valueFrom="1.0"
        android:valueTo="0.0"
        android:valueType="floatType" />
</set>

這裏在見還要看看Fragment所在的activity的佈局和Fragment自己的佈局:
activity

<RelativeLayout>
    <Button
        android:id="@+id/add_fragment_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Add fragment" />

    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/add_fragment_button"
        android:layout_marginTop="10dp"></FrameLayout>
</RelativeLayout>

fragment

<RelativeLayout>
    <TextView
        android:id="@+id/fragment_textview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="30sp" />
</RelativeLayout>

Activity的佈局是一個添加Fragment的按鈕,按鈕下面是一個Fragment的容器。Fragment的佈局就只包含一個TextView。

fun addFragmentToStack() {
    var newFragment = DemoFragemnt.newInstance("fragment $mStackLevel") //as Fragment

    var fragmentTransaction = fragmentManager.beginTransaction()
    fragmentTransaction.setCustomAnimations(R.animator.fragment_slide_left_enter,
            R.animator.fragment_slide_left_exit,
            R.animator.fragment_slide_right_enter,
            R.animator.fragment_slide_right_exit)
    fragmentTransaction.replace(R.id.fragment_container, newFragment)
    fragmentTransaction.addToBackStack(null)
    fragmentTransaction.commit()
}

Fragment的代碼很簡單,這裏直接略了。在Fragment的Transition裏調用方法fragmentTransaction.setCustomAnimations()來設置左邊的進入和退出,右邊的進入和退出動畫。按鈕點擊之後調用addFragmentToStack()方法replace Fragemnt,這樣動畫就展示出來了。

Android 5.0的Shared View

這個動畫在Android 5.0出現。也是屬於簡單易用型的。這個動畫能達到的效果是在一個Activity跳轉到另一個Activity的時候,兩個關聯的view會從第一個的外形轉換到第二個。兩個Activity則分別會fade out和fade in。效果很好,能夠省不少事。
先看看兩個Activity的佈局,以及他們如何share view的:

<RelativeLayout">

    <TextView
        android:id="@+id/first_textview"
        android:text="First shared view activity"
        android:textSize="25sp" />

    <TextView
        android:id="@+id/shared_text"
        android:layout_below="@id/first_textview"
        android:text="Shared"
        android:textSize="50sp" />
</RelativeLayout>

shared_text就是咱們準備要和另一個Activity關聯的view。這裏沒有什麼特別之處。繼續看下一個Activity的佈局:

<RelativeLayout>

    <TextView
        android:id="@+id/first_textview"
        android:text="Another shared view activity"
        android:textSize="25sp" />

    <TextView
        android:id="@+id/shared_text1111"
        android:text="Shared"
        android:textSize="50sp" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button"
        android:transitionName="shared_text" />
</RelativeLayout>

千萬不要被這裏的TextView迷惑了,他的ID是shared_text1111。下面的Button就纔是關聯view。Button裏有一個屬性android:transitionName它的值指向了上一個Activity的TextView。android:transitionName屬性關聯兩個Activity佈局中的兩個View。

接下來是Activity的代碼:

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

    var sharedText = findViewById(R.id.shared_text) as TextView
    sharedText.setOnClickListener { v ->
        var options = ActivityOptions.makeSceneTransitionAnimation(this@SharedViewActivity,
                sharedText, "shared_text")
        val i = Intent(this@SharedViewActivity, AnotherSharedViewActivity::class.java)
        startActivity(i, options.toBundle())
    }
}

看代碼,和上文中提到的Activity的過渡動畫很是相似。只不過這裏選擇的是用方法ActivityOptions.makeSceneTransitionAnimation()並在參數中指定了當前Activity中要share的view對象和這個對象的ID值。那麼另一個Activity呢?

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

在這個Activity中什麼都沒有作。因此,很是簡單。只須要使用transitionName這個屬性,並指向其餘的Activity的一個View的ID就能夠。

相關文章
相關標籤/搜索