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

主題是關于動畫的金抡,但是不是什么動畫的內容都包括。先泛泛的介紹一下腌且,然后詳細的介紹一下翻代碼找見的一個好玩的動畫的使用。以下的內容包括Android 3和Android 3.1等引入的API榛瓮,在使用中請注意版本铺董。

代碼都是用Kotlin寫的。如果你用的是新版的Android Studio禀晓。
創(chuàng)建項目之后精续,按下快捷鍵Shift+Ctrl+Alt+K就會自動把代碼從java轉換成Kotlin。
之后按照說明給項目配置Kotlin的插件即可粹懒。很簡單重付。Kotlin的官網(wǎng)在這里:http://kotlinlang.org/。

Property Animation

Android 3.0引入了這個動畫凫乖。理論上可以支持任意的對象(不限于View)在定義好的timer interval里修改其屬性值确垫,具體的介紹在這里。Property Animation的引入很好的改進了之前的Tween Animation的不足帽芽。

動畫和動畫監(jiān)聽器

ObjectAnimator繼承自ValueAnimator, ValueAnimator繼承自Animator删掀。屬性動畫一般使用ObjectAnimator來實現(xiàn)動畫。用ValueAnimator的話需要自己添加AnimatorUpdateListener來實現(xiàn)動畫中的每一個time interval的增量值导街。

Animator說可以添加AnimatorListener給他披泪,于是我們可以給Animator和他的子類添加AnimatorListener。這個listener在動畫的不同階段都會有對應的回調方法搬瑰。這些在上面提到的那篇文章中都有詳細的敘述款票,這里不再多說。

ViewPropertyAnimator

Android 3.1引入了ViewPropertyAnimator泽论,用這個類可以更加簡單的給View添加動畫艾少。

animate()方法返回了調用的view的ViewPropertyAnimator對象。這個對象可以同時執(zhí)行多個動畫佩厚。而且每一種動畫都提供了一組特定的方法姆钉。使用起來非常方便。比如:

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

這個動畫包括右移(translationX)、下移(translationY)和橫向放大兩倍(scaleX)潮瓶、縱向放大兩倍(scaleY)陶冷。最后的withLayer()方法可以用start()代替。只不過withLayer()會在情況允許的情況下調用硬件加速毯辅。

你也可以定義一個Runnable在動畫開始和結束的時候執(zhí)行埂伦。

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

動畫結束的時候執(zhí)行的Runnable

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

以上用了Kotlin的兩種不同的寫法思恐,但是意思都是一樣的沾谜,都是Runnable對象作為參數(shù)傳入方法。第一個寫法:初始化一個Runnable接口的匿名對象胀莹。這個對象用object關鍵字表明基跑。第二個寫法是,在Kotlin中只有一個方法的接口的實現(xiàn)描焰,可以直接把實現(xiàn)方法放在大括號里扔給傳入的方法作為參數(shù)媳否。

Kotlin是一個很有意思的語言,其與java的互操作非常的方便荆秦。他并不是一個運行在JVM上的特例獨行的語言篱竭,而是一個擁有腳本語言的便捷特點的Java。而且步绸,這個便捷不是像Java 8那樣的妥協(xié)以后的產(chǎn)物掺逼。更多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的定義。這兩個方法都是產(chǎn)生隨機數(shù)的:

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隔一段時間就執(zhí)行一次動畫病梢。這個動畫包括寬度、高度和上下的位移梁肿。為了不讓view的大小和位置變得沒譜蜓陌,所以控制寬高和位置的隨機數(shù)值是控制在一定的比例范圍內的。

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

布局動畫(Layout animation)

使用LayoutTransition類,可以實現(xiàn)在布局容器上添加一個動畫烛芬,并且在這個容器內的view樹的變化都會以動畫展現(xiàn)出來隧期。

這部分直接上代碼了飒责,各位跑起來就可以看到運行的結果。點擊按鈕仆潮,添加一個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類的實例就可以實現(xiàn)這個功能季希。而且不僅于此继控,在這個類的API中還包括一些諸如makeThumbnailScaleUpAnimation(View source, Bitmap thumbnail, int startX, int startY)以及makeThumbnailScaleDownAnimation(View source, Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener)等這樣的方法。比如在瀑布流中胖眷,從一個item點擊之后跳轉到其他Activity的動畫就可以用這兩個方法來實現(xiàn)。

Fragment的過渡動畫

說完了Activity在說說Fragment的過渡動畫霹崎。這部分完全可以另外起一篇博文來細說一下珊搀。簡要概括呢,設定動畫的代碼很容易尾菇,知識準備動畫內容多一點境析。下面的內容用到的Property Animation不再是代碼實現(xiàn),而是使用xml的方式實現(xiàn)派诬。

首先我們來討論一下這個過渡動畫應該是什么樣子的劳淆。在Fragment進入的時候逐漸顯示出來(alpha從0到1),位置從100到0(translation x從100到0)默赂,在Fragment退出的時候正好相反沛鸵。但是,進入和退出分兩個方向缆八∏或者需要我們設置兩個方向的進入和退出。分別是從左到右和從右到左的奈辰。

下面開始實戰(zhàn):在res目錄下創(chuàng)建一個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了袁,這樣動畫就展現(xiàn)出來了朗恳。

Android 5.0的Shared View

這個動畫在Android 5.0出現(xiàn)。也是屬于簡單易用型的载绿。這個動畫能達到的效果是在一個Activity跳轉到另外一個Activity的時候粥诫,兩個關聯(lián)的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關聯(lián)的view执赡。這里沒有什么特別之處。繼續(xù)看下一個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就才是關聯(lián)view。Button里有一個屬性android:transitionName它的值指向了上一個Activity的TextView跌帐。android:transitionName屬性關聯(lián)兩個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()并在參數(shù)中指定了當前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就可以盯仪。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蜜葱,隨后出現(xiàn)的幾起案子全景,更是在濱河造成了極大的恐慌,老刑警劉巖牵囤,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爸黄,死亡現(xiàn)場離奇詭異滞伟,居然都是意外死亡,警方通過查閱死者的電腦和手機炕贵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門梆奈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人称开,你說我怎么就攤上這事亩钟。” “怎么了鳖轰?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵清酥,是天一觀的道長。 經(jīng)常有香客問我蕴侣,道長焰轻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任昆雀,我火速辦了婚禮辱志,結果婚禮上,老公的妹妹穿的比我還像新娘狞膘。我一直安慰自己揩懒,他們只是感情好,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布挽封。 她就那樣靜靜地躺著旭从,像睡著了一般。 火紅的嫁衣襯著肌膚如雪场仲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天退疫,我揣著相機與錄音渠缕,去河邊找鬼。 笑死褒繁,一個胖子當著我的面吹牛亦鳞,可吹牛的內容都是我干的。 我是一名探鬼主播棒坏,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼燕差,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坝冕?” 一聲冷哼從身側響起徒探,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎喂窟,沒想到半個月后测暗,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體央串,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年碗啄,在試婚紗的時候發(fā)現(xiàn)自己被綠了质和。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡稚字,死狀恐怖饲宿,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情胆描,我是刑警寧澤瘫想,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站袄友,受9級特大地震影響殿托,放射性物質發(fā)生泄漏。R本人自食惡果不足惜剧蚣,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一支竹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鸠按,春花似錦礼搁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瑟曲,卻和暖如春饮戳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背洞拨。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工扯罐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人烦衣。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓歹河,卻偏偏與公主長得像,于是被迫代替她去往敵國和親花吟。 傳聞我的和親對象是個殘疾皇子秸歧,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內容