探索安卓中有意義的動畫!

ribot 致力於打造美好且充滿意義的用戶體驗,在這一過程當中,動畫不可或缺html

google

在 Droidcon London 聽完一場 激勵人心的演講以後, 筆者決定深刻研究安卓動畫。本文集中展現了其研究結果,但願使開發者和設計者們意識到,爲 Android 應用添加漂亮的動畫並不複雜。java

animate

動畫!android

若是你想嘗試這些動畫效果,本文全部實例都能在 Github 上的這款 Android 應用 中找到。git

筆者很是喜歡動畫效果,由於它不只提升用戶參與度,還能迅速奪人眼球。想一想那些以動畫設計著稱的應用,它們使用起來是多麼可心、流暢、天然github

falcon pro

Falcon Pro:即便細微的動畫效果也能夠對用戶體驗產生巨大影響。編程

如今,與那些你很喜歡但沒有動畫的應用作一番比較。性能優化

medium

Medium: 儘管筆者很喜好 medium APP,但它的確缺乏恰當的動畫。網絡

小動做也能造就大不一樣

咱們能夠從多個方面利用動畫,從而:app

  • 經過導航上下文傳輸用戶;框架

  • 強化元素的層級結構;

  • 展現屏幕顯示的組件變化。

本文旨在說明,在應用中實現有意義的動畫十分簡單可行——那麼,即刻開始吧。

觸覺反饋

在用戶觸摸屏幕時提供反饋,有助於視覺交流,造成互動。這些動畫不該分散用戶的注意力,但又使他們享受其中,得到清晰的視感,從而鼓勵進一步操做。

安卓框架爲此類反饋提供了波紋效果,經過設定視圖背景,便可使用:

?android:attr/selectableItemBackground-在視圖範圍內展現波紋效果;

ripple

波紋在接觸點開始,以後填充整個視圖背景。

?android:attr/selectableItemBackgroundBorderless –將波紋效果延伸至視圖以外。

ripple2

圓形波紋效果在接觸點開始,並沿半徑延伸至視圖以外。

View Property Animator

ViewPropertyAnimator 在 API 12 首次引入,容許咱們只使用一個Animator實例,就能夠簡單高效地使多個視圖屬性(並行地)執行動畫操做。

viewproperty

此處將繪製下文提到的全部動畫屬性。

注意: 若是已在視圖中設置了偵聽器,並打算在相同視圖下,實現其餘動畫且不使用回調函數,則須要將偵聽器設爲 null

用程序實現時,簡單又整潔:

mButton.animate()
        .alpha(1f)
        .scaleX(1f)
        .scaleY(1f)
        .translationZ(10f)
        .setInterpolator(new FastOutSlowInInterpolator())
        .setStartDelay(200)
        .setListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) { }

            @Override
            public void onAnimationEnd(Animator animation) { }

            @Override
            public void onAnimationCancel(Animator animation) { }

            @Override
            public void onAnimationRepeat(Animator animation) { }
        })
        .start();

注意: 其實咱們不須要在動畫生成器中調用 start( ) 方法,由於在中止聲明的同時,動畫就會自動啓動。在這種狀況下,只有在 UI toolkit 事件隊列開始下一次更新時,動畫纔會再開始。

alpha

製做 FAB 的 alpha 動畫值

x,y

FAB 的(X 和 Y 軸)座標動畫

z

FAB 的 Z 座標動畫

注意: 考慮到向後兼容性,你可使用ViewCompat 類,來實如今安卓 API 4 以及以上版本的ViewPropertyAnimator 類。

Object Animator

ViewPropertyAnimator 相似,ObjectAnimator 容許咱們在目標視圖(代碼和 XML 源文件中)的不一樣屬性中執行動畫。然而,它們仍是有些差別的:

  • 在每一個實例中,ObjectAnimator 只容許對單一屬性執行動畫。例如,座標 Y座標 X 變化;

  • 可是,它容許自定義屬性的動畫,例如視圖的前景色

使用自定義屬性給視圖作縮放動畫,並改變其前景色,能夠達成下圖的效果:

custom

使用自定義屬性時,能夠經過調用ObjectAnimator.ofInt(),建立一個ObjectAnimator實例,此處咱們聲明:

  • view – 應用動畫的視圖;

  • property – 設定動畫的屬性;

  • start color – 動畫視圖的初始顏色;

  • target color – 動畫視圖的目標顏色。

接下來,設置評估器(此處使用ArgbEvaluator 設置顏色動畫),設置延遲並執行 start( )

private void animateForegroundColor(@ColorInt final int targetColor) {
    ObjectAnimator animator = 
        ObjectAnimator.ofInt(YOUR_VIEW, FOREGROUND_COLOR, Color.TRANSPARENT, targetColor);
    animator.setEvaluator(new ArgbEvaluator());
    animator.setStartDelay(DELAY_COLOR_CHANGE);
    animator.start();
}

接下來,使用類似的方法作視圖縮放的動畫,主要區別在於:

  • 使用ObjectAnimator.ofFloat() 建立 ObjectAnimator 實例,由於在調整視圖大小時,並無改動整型值;

  • 使用 View.SCALE_X 和 View.SCALE_Y 視圖屬性,而非自定義屬性。

private void resizeView() {
        final float widthHeightRatio = (float)     getHeight() / (float) getWidth();
        resizeViewProperty(View.SCALE_X, .5f, 200);
        resizeViewProperty(View.SCALE_Y, .5f / widthHeightRatio, 250);
    }
    
    private void resizeViewProperty(Property<View, Float> property,
                                    float targetScale, 
                                    int durationOffset) {
        ObjectAnimator animator = ObjectAnimator.ofFloat(this, property, 1f, targetScale);
        animator.setInterpolator(new LinearOutSlowInInterpolator());
        animator.setStartDelay(DELAY_COLOR_CHANGE + durationOffset);
        animator.start();
    }

最後,將調整完大小的視圖移開屏幕。在這種狀況下,使用AdapterViewFlipper 容納離屏視圖,能夠對 ViewFlipper 實例調用showNext()方法,後者會使用(定義好的動畫處理該過程。接着,下一個視圖也會使用定義好的入場動畫,自動出如今屏幕上。

Interpolators

Interpolator 可用於定義動畫的變化率,意味着動畫的速度、加速度、行爲均可以改變。可用的 interpolator 有數種,且相互之間的差異微乎其微,建議讀者在設備上一探究竟。

fast1

該視圖以線型動做開始和結束動畫。

fast2

該視圖開始動做很快,逐漸降速直至結束。

linear

該視圖以線型動做開始,逐漸降速直至結束。

accelarate

該視圖在動畫開始時加速,並在接近結束時逐漸減速。

Circular Reveal

CircularReveal 使用剪切的圓形顯示或隱藏一組 UI 元素。該動畫除了帶來視覺上的連續性,還十分賞心悅目,有助於提升用戶參與度。

circular

如上圖所示,在視圖的動畫效果顯示以前,使用 ViewPropertyAnimator 隱藏浮動操做圖標。只需定義以下屬性就能夠配置 circular reveal:

  • startView – CircularReveal 的開始視圖(即壓縮視圖);

  • centerX –點擊視圖的 X軸中心;

  • centerY -點擊視圖的 Y軸中心;

  • targetView –要顯示的視圖;

  • finalRadius –剪切圓的半徑,大小等於以 X 中心和 Y 中心爲直角邊的三角形的斜邊的值。

int centerX = (startView.getLeft() + startView.getRight()) / 2;
int centerY = (startView.getTop() + startView.getBottom()) / 2;
float finalRadius = (float) Math.hypot((double) centerX, (double) centerY);
Animator mCircularReveal = ViewAnimationUtils.createCircularReveal(
  targetView, centerX, centerY, 0, finalRadius);

窗口轉換

定製用於活動間導航的轉換,可以使用戶對應用狀態產生更爲強烈的視覺聯繫。默認狀況可定製以下轉換:

  • enter –決定活動視圖如何進入場景;

  • exit -決定活動視圖如何退出場景;

  • reenter –決定活動視圖退出後如何再度進入;

  • shared elements –決定活動間如何共享視圖轉換。

API 21起,還有以下幾種新的轉換方式:

爆炸

Explode 轉換容許視圖從屏幕各個方位退出,會使壓縮視圖產生爆炸效果。

explode

在網格佈局中爆炸效果尤爲好。

這種效果易於實現——首先,須要在 res/transition 目錄中建立以下轉換:

<explode xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"/>

具體作法以下:

  • 聲明 explode 轉換;

  • 設置持續時間爲300毫秒。

接下來,須要將此設置爲活動的轉換。既能夠將其添加到活動主題:

<style name="AppTheme.Explode" parent="AppTheme.NoActionBar">
  <item name="android:windowExitTransition">@transition/slide_explode</item>
  <item name="android:windowReenterTransition">@android:transition/slide_top</item>
</style>

也可以編程的方式解決:

Transition explode = TransitionInflater.from(this).inflateTransition(R.transition.explode);
getWindow().setEnterTransition(explode);

滑動

滑動切換可使活動從屏幕右側或底部滑入/出。可能你之前有過相似的效果,可是這個新切換更加靈活。

slide

滑動切換使咱們依次滑動子視圖

這種轉換在切換活動時尤其常見,筆者對向右側滑的流暢感受情有獨鍾,固然這也很容易建立:

<slide xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:interpolator/decelerate_cubic"
    android:slideEdge="end"/>

在這裏:

  • 聲明瞭 slide 轉換;

  • 設置切換的slideEdgeend(右側),從而實現從右側開始滑動——若想要底部滑動將設置爲 bottom

漸變

漸變切換使活動轉換出現淡入或淡出的效果。

fade

在視圖中使用漸變更畫操做簡單,且效果宜人。

建立此切換的操做比以前的切換更加簡單:

<fade xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="300"/>

在這裏:

  • 聲明瞭 fade 轉換;

  • 設置持續時間爲300毫秒。

優化轉換

實驗的同時,筆者發現了一些能夠改善上述轉換效果的方法。

容許窗口頁面轉換——須要在主題中啓用下列屬性,主題都來源於一個資料主題:

<item name="android:windowContentTransitions">true</item>

啓用/禁用轉換重疊——上一轉換過程結束,新的頁面動畫纔會開始,這樣就會造成時延。在不一樣的案例中,若啓用以下屬性,轉換過程都會更加流暢天然:

<item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item>

排除特定視圖轉換—有時咱們並不想讓活動中的全部視圖參與動畫,並且大多數狀況下,工具欄和狀態欄是形成轉換故障主因。所幸,能夠排除特定的視圖,使之沒法轉換:

<explode xmlns:android="http://schemas.android.com/apk/res/android"
     android:duration="200">
        <targets>
            <target android:excludeId="@android:id/navigationBarBackground"/>
            <target android:excludeId="@android:id/statusBarBackground"/>
        </targets>
      </explode>

工具欄和操做欄——當使用操做欄的活動向使用工具欄的活動轉換時(反之亦然),轉換過程老是磕磕絆絆。爲此,應當確保轉換中的兩個活動都使用相同的組件。

轉換持續時間——既不能讓用戶等過久,也不能讓動畫轉換過快。這取決於轉換持續時間,最好經過試驗敲定恰當的時間。筆者發現,多數狀況下200-500微秒最爲合適。

共享元素轉換

共享元素轉換方便咱們爲頁面間的共享視圖製做動畫,使動畫更爲人性化,並給用戶帶來更好的視覺感覺。

shared

這裏,第一個頁面中的視圖縮小並平移至第二個頁面的標題圖片位置。

在佈局中,必須使用 transitionName 屬性將全部共享視圖聯繫起來——這代表了視圖間的轉換關係。下圖展現了以前動畫中的共享視圖:

link

這些都是共享視圖,意味着它們會在每次頁面轉換過程當中造成動畫。

爲了完成如上轉換,咱們首先要聲明共享轉換名稱,能夠經過使用 XML 佈局中的 transitionName 屬性來完成。

屏幕 1)

<RelativeLayout>
    <LinearLayout>

        <View 
            android:id="@+id/view_shared_transition"
            android:transitionName="@string/transition_view"/>

        <!-- Your other views -->

    </LinearLayout>
</RelativeLayout>

屏幕2)

<LinearLayout>

    <View
        android:id="@+id/view_shared_transition"
        android:transitionName="@string/transition_view"/>

    <View
        android:id="@+id/view_separator"/>

    <TextView
        android:id="@+id/text_detail"/>

    <TextView
        android:id="@+id/text_close"/>

</LinearLayout>

以後,在頁面1中建立 Pair 對象,使之包含轉換視圖與其 transitionName。而後將其傳給頁面選擇實例(ActivityOptionsCompat),由此兩個頁面都得知了共享組件,就能夠開始動畫了。

Pair participants = new Pair<>(mSquareView, ViewCompat.getTransitionName(mSquareView));

ActivityOptionsCompat transitionActivityOptions = 
        ActivityOptionsCompat.makeSceneTransitionAnimation(
                SharedTransitionsActivity.this, participants);

ActivityCompat.startActivity(SharedTransitionsActivity.this, 
                      intent, transitionActivityOptions.toBundle());

sliding

轉換的同時滑動這些視圖,有助於完成轉換。

以上就是兩個視圖間的轉換,那麼在第二個頁面中從底部滑入的視圖怎麼辦呢?

(它們就是左邊的那些視圖)

其實這個實現過程也很簡單,以下:

Slide slide = new Slide(Gravity.BOTTOM);
slide.addTarget(R.id.view_separator);
slide.addTarget(R.id.text_detail);
slide.addTarget(R.id.text_close);
getWindow().setEnterTransition(slide);

如你所見,建立一個新的Slide 轉換:將目標視圖添加到轉換中,並將滑動動做設爲入場動畫。

自定義轉換

咱們也可使用以前介紹過的 API 動畫建立本身的自定義轉換。例如,將共享元素轉換衍伸,成爲轉換視圖變體——當咱們須要顯示對話框(或者相似的彈框視圖)時,自定義轉換就會很是有用。具體以下所示:

custom2

該動畫能夠在組件狀態間引導用戶的注意力。

先來簡單瞭解一下上圖發生了什麼:

  • 首先建立一個SharedTransition,傳入壓縮視圖與轉換名稱以引用共享組件。

  • 而後建立ArcMotion 實例,使兩個視圖轉換時造成曲線動畫效果。

  • 接下來擴展 ChangeBounds 以建立自定義轉換,改變(morph)兩個形狀(對於button 和 FAB ,有兩個不一樣的類)。此處重寫了類中的多個方法,以便爲所需屬性作動畫。最後,使用 ViewPropertyAnimator 調整對話框的透明度,使用 ObjectAnimator 調整兩個視圖間的色彩,使用 AnimatorSet 實例將兩種動畫效果整合在一塊兒。

動態矢量圖片

API 21中(Lollipop),AnimatedVectorDrawable 可用於制定VectorDrawable 屬性的動畫,生成動態圖片。

vectors

在圖片上作幾種不一樣的動畫並不容易。

那麼如何完成呢,請看下圖:

vector2

該圖由幾個不一樣文件組成,首先建立兩個獨立的矢量文件,每一個都包含以下屬性:

  • Height & Width –矢量圖像的實際大小;

  • Viewport Height & Width –聲明描述矢量路徑的虛擬畫布的大小;

  • Group name –聲明路徑所屬的組名;

  • Pivot X & Y –聲明羣組規模和旋轉所使用的中心點;

  • Path Fill Color –描述矢量路徑的填充色;

  • Path Data –聲明用於繪製矢量的矢量路徑數據。

注意: 全部被引用的屬性都存儲在 general strings file 中,這樣能夠保持程序整潔美觀。

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:height="56dp" 
        android:width="56dp"
        android:viewportHeight="24.0"
        android:viewportWidth="24.0">
    <group
        android:name="@string/groupAddRemove"
        android:pivotX="12"
        android:pivotY="12">
        <path 
            android:fillColor="@color/stroke_color"
            android:pathData="@string/path_add"/>
    </group>
</vector>

vector3

該矢量由 ic_add.xml 文件(以下所示)生成

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:height="56dp"
        android:width="56dp"
        android:viewportHeight="24.0"
        android:viewportWidth="24.0">
    <group
        android:name="@string/groupAddRemove"
        android:pivotX="12"
        android:pivotY="12">
        <path 
            android:fillColor="@color/stroke_color" 
            android:pathData="@string/path_remove"/>
    </group>
</vector>

vectors

該矢量由ic_remove.xml 文件(以下所示)生成

接下來聲明 Animated Vector Drawable 文件,其中包含 Vector Drawable 和每一個圖片狀態動畫(AddRemove)的聲明。檢查從 AddRemove 的矢量動畫,聲明一個目標文件(target)以完成:

  • 狀態轉換的動畫;

  • 圖片旋轉的動畫。

<animated-vector android:drawable="@drawable/ic_add">
        <target
            android:name="@string/add"
            android:animation="@animator/add_to_remove" />
         <target
            android:name="@string/groupAddRemove"
            android:animation="@animator/rotate_add_to_remove" />
    </animated-vector>

而後建立目標文件中引用的每一個文件。

改變圖片狀態

add_to_remove.xml 文件中,使用ObjectAnimator 改變圖形形狀,其中會用到以下屬性:

  • propertyName –執行動畫的屬性;

  • valueFrom –矢量路徑的初始值;

  • valueTo –矢量路徑的目標值;

  • duration –動畫持續時間;

  • interpolator –動畫插值器;

  • valueType –動畫值類型。

<objectAnimator
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:propertyName="pathData"
        android:valueFrom="@string/path_add"
        android:valueTo="@string/path_remove"
        android:duration="@integer/duration"
        android:interpolator="@android:interpolator/fast_out_slow_in"
        android:valueType="pathType" />

形狀旋轉

可以使用類似的方法旋轉圖像,只是會用到旋轉屬性和旋轉值:

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

執行相反的動畫(從 RemoveAdd)所需的操做方法相同,只不過將動畫值反置。

rotating

完成後的動態矢量圖,效果很不錯吧?

使用OneAPM分析UI卡頓

使用 OneAPM 能夠快速定位分析UI性能,Mobile Insight的卡頓能夠直觀地展現這些信息。
卡頓趨勢圖
能夠分析繪製APP卡頓趨勢圖,精肯定位每1秒內的繪圖刷新信號中斷的次數,從多維度分析卡頓現象,如APP版本、操做系統版本的分佈狀況等。
卡頓分佈
卡頓詳情列表展現:訪問時間,發生卡頓時的流暢度,耗時,發生卡頓時的設備信息,APP版本,操做系統及版本,CPU信息
經過分析該頁面信息能夠清楚瞭解到卡頓來源,以便針對性快速優化。
卡頓詳情

動畫卡頓緣由

動畫卡頓的緣由大概有這樣三種,這些因素將直接影響動畫的性能,致使卡頓。即:

  1. 手勢滑動速度

  2. 幀率

  3. 觸摸事件響應的速度

手勢滑動、幀率是跟各類手機設備有直接的關係,各類各樣的硬件設備會表現出不同的性能,若是從這個方面入手考慮優化,就十分須要 OneAPM Mobile Insight 這樣的從多維度來分析性能的一款工具。

結語

雖然只是淺談,文本旨在圍繞建立有意義的動畫提供有益的視角,使讀者受益。從此,筆者會繼續努力,以求進一步改善應用的外觀與用戶體驗。

原文地址:https://medium.com/ribot-labs/exploring-meaningful-motion-on-android-1cd95a4bc61d#.vqazussmj

OneAPM Mobile Insight 以真實用戶體驗爲度量標準進行 Crash 分析,監控網絡請求及網絡錯誤,提高用戶留存。訪問 OneAPM 官方網站感覺更多應用性能優化體驗,想閱讀更多技術文章,請訪問 OneAPM 官方技術博客

相關文章
相關標籤/搜索