iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 23
2
Software Development

Android animation 30天上手系列 第 23

Day23 VectorDrawable Animation 範例:play-pause

  • 分享至 

  • xImage
  •  

前面花了3篇的篇輻來講VectorDrawable 及 VectorDrawable animation。

我們就來做一個從Play變Pause的動畫,用慢動作來看如下圖:

demo

1.為了讓從暫停到撥放有旋轉90度的效果,所以我們將撥放的Icon做成朝上的三角形
2.為了讓2個Icon可以變成動畫,我們知道pathData要是成對的,所以我們把三角形分為2半來處理,三角形的左右兩半會對映到Pause的左右兩塊

PathData
Play:M12,16 H5 L8.5,10.5 L12,5 M12,16 H19 L15.5,10.5 L12,5
pause:M10,18 H6 L6,6 L10,6 M14,18 H18 L18,6 L14,6

play_pause

先來看一下我們在drawable放了哪些檔案
source

icon_Play.xml
因為我們在pathData裡畫的是朝上的三角形,這裡加上rotation=90,讓三角形變成play的樣子。

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="120dp"
    android:height="120dp"

    android:viewportHeight="24"
    android:viewportWidth="24">

    <group
        android:name="iconGroup"
        android:pivotX="12"
        android:pivotY="12"
        android:rotation="90">

        <path
            android:name="iconPath"
            android:fillColor="#FF000000"
            android:pathData="@string/play_path" />
    </group>
</vector>

icon_pause.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="120dp"
    android:height="120dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">

    <group
        android:name="iconGroup"
        android:pivotX="12"
        android:pivotY="12"
        >
    <path
        android:name="iconPath"
        android:fillColor="#FF000000"
        android:pathData="@string/pause_path" />
    </group>
</vector>

從play到pause的動畫

第1個objectAnimator:旋轉從90度轉到180度
為什麼從90度開始呢,因為一開始我們就將icon_paly旋轉90為play的三角形了。從90旋轉至180會讓play從朝上的三角形變成朝右的,會讓pause轉180度還是一樣。

第二個objectAnimator
pathData的轉換,由play轉為pause

adv_play_to_pause.xml

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    xmlns:tools="http://schemas.android.com/tools"
    android:drawable="@drawable/ic_pause"
    tools:targetApi="lollipop">
    <target android:name="iconGroup">
        <aapt:attr name="android:animation">
            <set>
                <objectAnimator
                    android:duration="4000"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="rotation"
                    android:valueFrom="90"
                    android:valueTo="180" />
            </set>
        </aapt:attr>
    </target>

    <target android:name="iconPath">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="4000"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="pathData"
                android:valueFrom="@string/play_path"
                android:valueTo="@string/pause_path"
                android:valueType="pathType" />
        </aapt:attr>
    </target>
</animated-vector>

從pause到play的動畫

第1個objectAnimator
旋轉從0轉到90度
第二個objectAnimator
pathData的轉換,由pause轉為play

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:drawable="@drawable/ic_play"
    >
    <target android:name="iconGroup">
        <aapt:attr name="android:animation">
            <set>
                <objectAnimator
                    android:duration="4000"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="rotation"
                    android:valueFrom="0"
                    android:valueTo="90" />
            </set>
        </aapt:attr>
    </target>

    <target android:name="iconPath">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="4000"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="pathData"
                android:valueFrom="@string/pause_path"
                android:valueTo="@string/play_path"
                android:valueType="pathType" />
        </aapt:attr>
    </target>
</animated-vector>

這邊可以發現,我們play的pathData與pause的pathData放在strings.xml,因為在icon、animation都會用到。

<resources>
    <string name="pause_path">M10,18 H6 L6,6 L10,6 M14,18 H18 L18,6 L14,6</string>
    <string name="play_path">M12,16 H5 L8.5,10.5 L12,5 M12,16 H19 L15.5,10.5 L12,5</string>
</resources>

最後,animated-selector裡就是play與pause的icon與transition了。

as_playpause.xml

<animated-selector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    >

    <item
        android:id="@+id/pause"
        android:drawable="@drawable/ic_pause"
        app:state_pause="true" />

    <item
        android:id="@+id/play"
        android:drawable="@drawable/ic_play" />

    <transition
        android:drawable="@drawable/avd_pause_to_play"
        android:fromId="@id/pause"
        android:reversible="false"
        android:toId="@id/play" />

    <transition
        android:drawable="@drawable/avd_play_to_pause"
        android:fromId="@id/play"
        android:reversible="false"
        android:toId="@id/pause" />

</animated-selector>

activity_main.xml,加上ImageView

<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"
    android:gravity="center"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/play"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@drawable/as_playpause" />

</LinearLayout>

在MainActivity切換state

class MainActivity : AppCompatActivity() {
    private val STATE_PLAY = intArrayOf(R.attr.state_play, -R.attr.state_pause)
    private val STATE_PAUSE = intArrayOf(-R.attr.state_play, R.attr.state_pause)

    var isPlay = true
    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        play.setImageState(STATE_PLAY, true)

        play.setOnClickListener {
            if (isPlay) {
                play.setImageState(STATE_PAUSE, true)
            } else {
                play.setImageState(STATE_PLAY, true)
            }

            isPlay = !isPlay
        }
    }
}

完整程式:
https://github.com/evanchen76/PlayPauseAnimation

線上課程:
Android 動畫入門到進階
Android UI 進階實戰(Material Design Component)

出版書:
Android TDD 測試驅動開發:從 UnitTest、TDD 到 DevOps 實踐


上一篇
Day22 VectorDrawable Animation TrimPath
下一篇
Day24 VectorDrawable Animation 匯入動畫
系列文
Android animation 30天上手30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言