ribot 致力於打造美好且充滿意義的用戶體驗,在這一過程當中,動畫不可或缺。html
在 Droidcon London 聽完一場 激勵人心的演講以後, 筆者決定深刻研究安卓動畫。本文集中展現了其研究結果,但願使開發者和設計者們意識到,爲 Android 應用添加漂亮的動畫並不複雜。java
動畫!android
若是你想嘗試這些動畫效果,本文全部實例都能在 Github 上的這款 Android 應用 中找到。git
筆者很是喜歡動畫效果,由於它不只提升用戶參與度,還能迅速奪人眼球。想一想那些以動畫設計著稱的應用,它們使用起來是多麼可心、流暢、天然。github
Falcon Pro:即便細微的動畫效果也能夠對用戶體驗產生巨大影響。編程
如今,與那些你很喜歡但沒有動畫的應用作一番比較。性能優化
Medium: 儘管筆者很喜好 medium APP,但它的確缺乏恰當的動畫。網絡
咱們能夠從多個方面利用動畫,從而:app
經過導航上下文傳輸用戶;框架
強化元素的層級結構;
展現屏幕顯示的組件變化。
本文旨在說明,在應用中實現有意義的動畫十分簡單可行——那麼,即刻開始吧。
在用戶觸摸屏幕時提供反饋,有助於視覺交流,造成互動。這些動畫不該分散用戶的注意力,但又使他們享受其中,得到清晰的視感,從而鼓勵進一步操做。
安卓框架爲此類反饋提供了波紋效果,經過設定視圖背景,便可使用:
?android:attr/selectableItemBackground-在視圖範圍內展現波紋效果;
波紋在接觸點開始,以後填充整個視圖背景。
?android:attr/selectableItemBackgroundBorderless –將波紋效果延伸至視圖以外。
圓形波紋效果在接觸點開始,並沿半徑延伸至視圖以外。
ViewPropertyAnimator 在 API 12 首次引入,容許咱們只使用一個Animator實例,就能夠簡單高效地使多個視圖屬性(並行地)執行動畫操做。
此處將繪製下文提到的全部動畫屬性。
alpha() –設定動畫的 alpha 值;
translationZ()-在 Z 軸上平移視圖;
setDuration() –設置動畫時長;
setStartDelay() –設置動畫延時;
setInterpolator() – 設置動畫插值;
setListener() – 爲動畫的開始、結束、重複、取消設置偵聽器。
注意: 若是已在視圖中設置了偵聽器,並打算在相同視圖下,實現其餘動畫且不使用回調函數,則須要將偵聽器設爲 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 事件隊列開始下一次更新時,動畫纔會再開始。
製做 FAB 的 alpha 動畫值
FAB 的(X 和 Y 軸)座標動畫
FAB 的 Z 座標動畫
注意: 考慮到向後兼容性,你可使用ViewCompat 類,來實如今安卓 API 4 以及以上版本的ViewPropertyAnimator 類。
和 ViewPropertyAnimator 相似,ObjectAnimator 容許咱們在目標視圖(代碼和 XML 源文件中)的不一樣屬性中執行動畫。然而,它們仍是有些差別的:
在每一個實例中,ObjectAnimator 只容許對單一屬性執行動畫。例如,座標 Y 隨座標 X 變化;
可是,它容許自定義屬性的動畫,例如視圖的前景色。
使用自定義屬性給視圖作縮放動畫,並改變其前景色,能夠達成下圖的效果:
使用自定義屬性時,能夠經過調用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()方法,後者會使用(定義好的動畫處理該過程。接着,下一個視圖也會使用定義好的入場動畫,自動出如今屏幕上。
Interpolator 可用於定義動畫的變化率,意味着動畫的速度、加速度、行爲均可以改變。可用的 interpolator 有數種,且相互之間的差異微乎其微,建議讀者在設備上一探究竟。
No Interpolator -該視圖動畫沒有變化率;
該視圖以線型動做開始和結束動畫。
該視圖開始動做很快,逐漸降速直至結束。
該視圖以線型動做開始,逐漸降速直至結束。
該視圖在動畫開始時加速,並在接近結束時逐漸減速。
Accelerate –視圖逐漸加速直到動畫結束;
Decelerate –視圖逐漸減速直到動畫結束;
Anticipate –在以標準方式開始動畫以前,視圖先進行輕微反轉;
Anticipate-Overshoot –與 Anticipate 相似,但動畫過程當中,回拉動做更爲誇張;
BounceInterpolator – 視圖動畫結束以前會有‘反彈’效果;
LinearInterpolator – 視圖以線型平滑的動畫開始,直到結束;
OvershootInterpolator – 視圖動畫先放大給定值,再縮回原值。
CircularReveal 使用剪切的圓形顯示或隱藏一組 UI 元素。該動畫除了帶來視覺上的連續性,還十分賞心悅目,有助於提升用戶參與度。
如上圖所示,在視圖的動畫效果顯示以前,使用 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 轉換容許視圖從屏幕各個方位退出,會使壓縮視圖產生爆炸效果。
在網格佈局中爆炸效果尤爲好。
這種效果易於實現——首先,須要在 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 xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:interpolator/decelerate_cubic" android:slideEdge="end"/>
在這裏:
聲明瞭 slide 轉換;
設置切換的slideEdge爲end(右側),從而實現從右側開始滑動——若想要底部滑動將設置爲 bottom。
漸變切換使活動轉換出現淡入或淡出的效果。
在視圖中使用漸變更畫操做簡單,且效果宜人。
建立此切換的操做比以前的切換更加簡單:
<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微秒最爲合適。
共享元素轉換方便咱們爲頁面間的共享視圖製做動畫,使動畫更爲人性化,並給用戶帶來更好的視覺感覺。
這裏,第一個頁面中的視圖縮小並平移至第二個頁面的標題圖片位置。
在佈局中,必須使用 transitionName 屬性將全部共享視圖聯繫起來——這代表了視圖間的轉換關係。下圖展現了以前動畫中的共享視圖:
這些都是共享視圖,意味着它們會在每次頁面轉換過程當中造成動畫。
爲了完成如上轉換,咱們首先要聲明共享轉換名稱,能夠經過使用 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());
轉換的同時滑動這些視圖,有助於完成轉換。
以上就是兩個視圖間的轉換,那麼在第二個頁面中從底部滑入的視圖怎麼辦呢?
(它們就是左邊的那些視圖)
其實這個實現過程也很簡單,以下:
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 動畫建立本身的自定義轉換。例如,將共享元素轉換衍伸,成爲轉換視圖變體——當咱們須要顯示對話框(或者相似的彈框視圖)時,自定義轉換就會很是有用。具體以下所示:
該動畫能夠在組件狀態間引導用戶的注意力。
先來簡單瞭解一下上圖發生了什麼:
首先建立一個SharedTransition,傳入壓縮視圖與轉換名稱以引用共享組件。
而後建立ArcMotion 實例,使兩個視圖轉換時造成曲線動畫效果。
接下來擴展 ChangeBounds 以建立自定義轉換,改變(morph)兩個形狀(對於button 和 FAB ,有兩個不一樣的類)。此處重寫了類中的多個方法,以便爲所需屬性作動畫。最後,使用 ViewPropertyAnimator 調整對話框的透明度,使用 ObjectAnimator 調整兩個視圖間的色彩,使用 AnimatorSet 實例將兩種動畫效果整合在一塊兒。
在 API 21中(Lollipop),AnimatedVectorDrawable 可用於制定VectorDrawable 屬性的動畫,生成動態圖片。
在圖片上作幾種不一樣的動畫並不容易。
那麼如何完成呢,請看下圖:
該圖由幾個不一樣文件組成,首先建立兩個獨立的矢量文件,每一個都包含以下屬性:
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>
該矢量由 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>
該矢量由ic_remove.xml 文件(以下所示)生成
接下來聲明 Animated Vector Drawable 文件,其中包含 Vector Drawable 和每一個圖片狀態動畫(Add 或 Remove)的聲明。檢查從 Add 到 Remove 的矢量動畫,聲明一個目標文件(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" />
執行相反的動畫(從 Remove 到 Add)所需的操做方法相同,只不過將動畫值反置。
完成後的動態矢量圖,效果很不錯吧?
使用 OneAPM 能夠快速定位分析UI性能,Mobile Insight的卡頓能夠直觀地展現這些信息。
能夠分析繪製APP卡頓趨勢圖,精肯定位每1秒內的繪圖刷新信號中斷的次數,從多維度分析卡頓現象,如APP版本、操做系統版本的分佈狀況等。
卡頓詳情列表展現:訪問時間,發生卡頓時的流暢度,耗時,發生卡頓時的設備信息,APP版本,操做系統及版本,CPU信息
經過分析該頁面信息能夠清楚瞭解到卡頓來源,以便針對性快速優化。
動畫卡頓的緣由大概有這樣三種,這些因素將直接影響動畫的性能,致使卡頓。即:
手勢滑動速度
幀率
觸摸事件響應的速度
手勢滑動、幀率是跟各類手機設備有直接的關係,各類各樣的硬件設備會表現出不同的性能,若是從這個方面入手考慮優化,就十分須要 OneAPM Mobile Insight 這樣的從多維度來分析性能的一款工具。
雖然只是淺談,文本旨在圍繞建立有意義的動畫提供有益的視角,使讀者受益。從此,筆者會繼續努力,以求進一步改善應用的外觀與用戶體驗。
原文地址:https://medium.com/ribot-labs/exploring-meaningful-motion-on-android-1cd95a4bc61d#.vqazussmj
OneAPM Mobile Insight 以真實用戶體驗爲度量標準進行 Crash 分析,監控網絡請求及網絡錯誤,提高用戶留存。訪問 OneAPM 官方網站感覺更多應用性能優化體驗,想閱讀更多技術文章,請訪問 OneAPM 官方技術博客。