標籤雲

搜尋此網誌

2015/10/12

Material Design - Animations

Material theme 本身就有對於按鈕或 Activity 切換提供預設的動畫
而 Android 5.0 (API level 21) 之後則可以對於以下動畫進行客製化:

Touch feedback

Button 的預設 Touch feedback 是使用 RippleDrawable
若想套用這個動畫效果可以把 xml 裡的 background 屬性設為
?android:attr/selectableItemBackground (View 本身範圍內的波紋)
或 API level 21 的新屬性 ?android:attr/selectableItemBackgroundBorderless (View 本身上面, parent 範圍內的波紋)
或是用 xml 以 ripple 標籤定義 RippleDrawable

ripple 的顏色可以用 android:colorControlHighlight 設定

Circular Reveal

Reveal 動畫可以在顯示或隱藏一組 UI 時為使用者提供視覺延續性
ViewAnimationUtils 只有一個 Public Methods
ViewAnimationUtils.createCircularReveal(View view, int centerX, int centerY, float startRadius, float endRadius)
它可以回傳一個圓形裁剪動畫的 Animator

來看一下範例
//  invisible 的 view
View myView = findViewById(R.id.my_view);

// 取得 view 的中心位置做為 clipping circle 的圓心
int cx = myView.getWidth() / 2;
int cy = myView.getHeight() / 2;

// 取 view 的長邊尺寸做為 clipping circle 的最終半徑大小
int finalRadius = Math.max(myView.getWidth(), myView.getHeight());

// 呼叫 createCircularReveal (startRadius 是 0)
Animator anim =
    ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);

// 將設為 visible 然後進行動畫
myView.setVisibility(View.VISIBLE);
anim.start();

如果是隱藏的話就要改成這樣
Animator anim =
    ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);

// 在 onAnimationEnd 時把 view 設為 invisible
anim.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
        myView.setVisibility(View.INVISIBLE);
    }
});

anim.start();


Activity transitions
Activity 間的轉換
可以自訂 enter 跟 exit 以及 shared elements 的 transitions

Android 5.0 (API level 21) 支援的 enter 跟 exit transitions 有:
explode (中心放大/縮小)
slide (滑入/滑出)
fade (淡入/淡出)

只要是繼承自 Visibility 的 Transition 類別都可以做為 enter 跟 exit 的動畫

支援 shared elements 的 transitions 有:
changeBounds (邊界)
changeClipBounds (邊界裁剪)
changeTransform (scale 跟 rotation)
changeImageTransform (圖像的 size 跟 scale)

<style name="BaseAppTheme" parent="android:Theme.Material">
  <!-- enable window content transitions -->
  <item name="android:windowContentTransitions">true</item>

  <!-- specify enter and exit transitions -->
  <item name="android:windowEnterTransition">@transition/slide</item>
  <item name="android:windowExitTransition">@transition/slide</item>

  <!-- specify shared element transitions -->
  <item name="android:windowSharedElementEnterTransition">
    @transition/shared_element_transition</item>
  <item name="android:windowSharedElementExitTransition">
    @transition/shared_element_transition</item>
</style>

transition 的 xml 範例如下
shared_element_transition.xml
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:transitionOrdering="together">
    <changeBounds />
    <changeTransform />
    <changeImageTransform />
</transitionSet>

transition 如果不用 theme 指定的話
也可以用 code 來設定
但要記得 requestFeature
// inside your activity (if you did not enable transitions in your theme)
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

getWindow().setEnterTransition(new Explode());
Window.setSharedElementEnterTransition();

Window.setSharedElementExitTransition();
getWindow().setExitTransition(new Explode());

另外 Window.setAllowEnterTransitionOverlap(true) 可以讓 enter transition 盡快進行

Activity 切換的範例 code 如下:
getWindow().setExitTransition(new Explode());
Intent intent = new Intent(this, MyOtherActivity.class);
startActivity(intent,
    ActivityOptions.makeSceneTransitionAnimation(this).toBundle());

有 sharedElement 的話請參考這裡:
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
        Pair.create(view1, "agreedName1"),
        Pair.create(view2, "agreedName2"));
// start the new activity
startActivity(intent, options.toBundle());

Curved motion
Android 5.0 (API level 21) 之後可自訂動畫的時間曲線及補間曲線
新的 PathInterpolator 類別是基於貝茲區線 (Bézier curve) 的 interpolator 或 Path 物件
曲線定義在 1x1 範圍的座標(0, 0)到(1, 1)

自訂 pathInterpolator 的 xml 範例如下:
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
    android:controlX1="0.4"
    android:controlY1="0"
    android:controlX2="1"
    android:controlY2="1"/>
</style>

material design specification 中
系統提供了三種基本曲線的 XML resources:

@interpolator/fast_out_linear_in.xml
@interpolator/fast_out_slow_in.xml
@interpolator/linear_out_slow_in.xml

設定 interpolator 的方法是將 PathInterpolator 物件傳入 Animator.setInterpolator 中
Animator.setInterpolator (TimeInterpolator value)

ObjectAnimator 也提供了一些新的 constructor 讓我們傳入 Path 物件, 例如:
ObjectAnimator mAnimator;
mAnimator = ObjectAnimator.ofFloat(view, View.X, View.Y, path);
...
mAnimator.start();

View state changes

StateListAnimator 讓我們可以在 view 的 state change 間進行動畫

<!-- animate the translationZ property of a view when pressed -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_pressed="true">
    <set>
      <objectAnimator android:propertyName="translationZ"
        android:duration="@android:integer/config_shortAnimTime"
        android:valueTo="2dp"
        android:valueType="floatType"/>
        <!-- you could have other objectAnimator elements
             here for "x" and "y", or other properties -->
    </set>
  </item>
  <item android:state_enabled="true"
    android:state_pressed="false"
    android:state_focused="true">
    <set>
      <objectAnimator android:propertyName="translationZ"
        android:duration="100"
        android:valueTo="0"
        android:valueType="floatType"/>
    </set>
  </item>
</selector>
用法為將這樣的 xml 透過 android:stateListAnimator 屬性指給 view
在 code 裡則是透過 AnimationInflater.loadStateListAnimator (Context context, int id) 將 xml 的 StateListAnimator 載入進來
再用 View.setStateListAnimator(StateListAnimator stateListAnimator) 設定

由於 material theme 的 button 都有 Z 屬性
android:stateListAnimator 如果傳入 @null 則 Z 屬性不會有效

AnimatedStateListDrawable 類別則是 state 間的 drawable 動畫
有些 Android 5.0 的 widget 也是利用這樣的動畫方式
範例如下:
<!-- res/drawable/myanimstatedrawable.xml -->
<animated-selector
    xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- provide a different drawable for each state-->
    <item android:id="@+id/pressed" android:drawable="@drawable/drawableP"
        android:state_pressed="true"/>
    <item android:id="@+id/focused" android:drawable="@drawable/drawableF"
        android:state_focused="true"/>
    <item android:id="@id/default"
        android:drawable="@drawable/drawableD"/>

    <!-- specify a transition -->
    <transition android:fromId="@+id/default" android:toId="@+id/pressed">
        <animation-list>
            <item android:duration="15" android:drawable="@drawable/dt1"/>
            <item android:duration="15" android:drawable="@drawable/dt2"/>
            ...
        </animation-list>
    </transition>
    ...
</animated-selector>

這篇好長阿~~~
最後來看一下 AnimatedVectorDrawable

在 xml 檔案中定義 animated vector 有三個要素

1. res/drawable/ 中以 vector 標籤為 root 的 xml 檔
注意它是由裡面的 group 跟 path 標籤組成
而 group 跟 path 標籤都要指定 android:name 屬性才可以
<!-- res/drawable/vectordrawable.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="64dp"
    android:width="64dp"
    android:viewportHeight="600"
    android:viewportWidth="600">
    <group
        android:name="rotationGroup"
        android:pivotX="300.0"
        android:pivotY="300.0"
        android:rotation="45.0" >
        <path
            android:name="v"
            android:fillColor="#000000"
            android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
    </group>
</vector>

2. res/drawable/ 中以 animated-vector 標籤為 root 的 xml 檔
把 vector 的 xml 檔設定在 android:drawable 屬性
動畫則由 android:animation 屬性來設定
<!-- res/drawable/animvectordrawable.xml -->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
  android:drawable="@drawable/vectordrawable" >
    <target
        android:name="rotationGroup"
        android:animation="@anim/rotation" />
    <target
        android:name="v"
        android:animation="@anim/path_morph" />
</animated-vector>

3. res/anim/ 中有一個或數個 objectAnimator 標籤的 xml 檔
用來定義動畫
<!-- res/anim/path_morph.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="3000"
        android:propertyName="pathData"
        android:valueFrom="M300,70 l 0,-70 70,70 0,0   -70,70z"
        android:valueTo="M300,70 l 0,-70 70,0  0,140 -70,0 z"
        android:valueType="pathType" />
</set>

相關資料:
Defining Custom Animations

沒有留言: