原文鏈接 : Music Player: From UI Proposal to Code(需科學上網)
原文作者 : André Mion
譯者 : Siegen
一些開發者在遇到稍顯復雜的UI方案時,會覺得很難編碼實現。他們中的許多人會在寫代碼的時候跳過重要的UI或者動畫,這導致最終的效果和原始方案相差甚遠。
本文主要談論的是針對一個UI方案如何編碼的過程,在這個過程中跳過了一些基本的Android細節,主要聚焦于UI的過渡和動畫上。
*** MaterialUp***
這是一個很棒的網站,設計師與開發者能夠在這里找到和分享用于構建Material Design網站或app的資源。這里有許多用戶界面設計,有趣的試驗,開源app,庫以及一些在Android,Web或者iOS上都很好用的產品。
瀏覽這個網站你可以找到這個由Anish Chandran創建的名為 Music Player 的用戶界面資源。
這個設計方案給了我們一個好例子,它展示了一個音樂播放應用該如何以流暢和連續的方式運用Material和Motion 設計。
熱身#
首先,我們需要做一些準備工作。
a. 把動態方案分解成幀
把動態方案文件轉換成一個個單獨的幀。這可以幫助我們查看動畫與過渡的每一個步驟。
b. 按類型分解
我們有許多的視圖都會有動畫與過渡同時進行的情況,同時去思考這些內容,會讓寫代碼變得非常的困難。我們可以把這些過渡與動畫按照類型分解,比如:view滑動到底部,view淡出,view過渡到新的Activity,等等。
不管有沒有動畫,接下來的這個建議都值得在每一個布局中采用。
c. 簡化你的視圖結構
創建一個盡可能簡單的視圖結構,避免在同一個布局里使用過多的容器ViewGroup。這樣可以使過渡動畫的設計變得簡單,使得維護起來更加方便,而且這樣做會大幅度的提高應用在動畫過程中的運行性能。
魔法是怎樣發生的
在布局文件中,一些ViewGroup已經把 android:transitionGroup屬性設置為true,這樣可以讓ViewGroup在Activity Transition期間被作為一個整體對待。比如這里的播放列表容器(main layout file)和控制按鈕容器(detail layout file)。
<RelativeLayout
android:id="@+id/playlist"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/cover"
android:gravity="center_vertical"
android:padding="@dimen/activity_vertical_margin"
android:transitionGroup="true">
…
<LinearLayout
android:id="@+id/controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:gravity="center_horizontal"
android:transitionGroup="true"
app:layout_marginBottomPercent="5%">
…
在styles.xml 中有我們在 MainActivity 和Detail Activity里面使用的主題.
-
AppTheme.Main
<item name="android:windowSharedElementsUseOverlay">false</item>
禁用共享元素視圖的overlay。在 Music Player的布局中我們需要在shared element view從Main Activity到Detail Activity移動的時候禁用overlay。如果它是啟用的,某些共享元素視圖可能會以錯誤的方式覆蓋到其它view上。
<pre><code><item name="android:windowExitTransition">@transition/list_content_exit_transition</item>
<item name="android:windowReenterTransition">@transition/list_content_reenter_transition</item>
</pre></code>
列表內容的退出和進入具有相同的過渡方式。
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@integer/anim_duration_default"
// 1
android:startDelay="@integer/anim_duration_default">
// 2
<fade>
<targets>
<target android:targetId="@id/pane" />
</targets>
</fade>
// 3
<slide android:slideEdge="bottom">
<targets>
<target android:excludeId="@android:id/statusBarBackground" />
<target android:excludeId="@id/pane" />
<target android:excludeId="@android:id/navigationBarBackground" />
</targets>
</slide>
</transitionSet> ```
1. 設置一個延遲讓這些過渡和FAB的漸變動畫保持同步。
2. 通過targetId屬性指定的pane視圖淡出或者淡入。
3. 把RecyclerView和播放列表滑倒底部,并使用excludeId屬性把狀態欄,pane以及navigation bar 排除在外。
<pre><code><item name="android:windowSharedElementExitTransition">@transition/list_shared_element_exit_transition</item>
<item name="android:windowSharedElementReenterTransition">@transition/list_shared_element_reenter_transition</item></pre></code>

播放按鈕退出和重新進入的transition方法幾乎相同。
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:duration="@integer/anim_duration_default">
// 1
<transition
class="com.sample.andremion.musicplayer.transition.PlayButtonTransition"
app:mode="play|pause" />
</transitionSet>
1.[PlayButtonTransition](https://github.com/andremion/Music-Player/blob/master/app/src/main/java/com/sample/andremion/musicplayer/transition/PlayButtonTransition.java) 是一個自定義的transition,它封裝了一個[AnimatedVectorDrawable](https://github.com/andremion/Android-Animated-Icons) 來把播放按鈕變為暫停或者反過來操作,這取決于mode的值。
* AppTheme.Detail
<pre><code><item name="android:windowEnterTransition">@transition/detail_content_enter_transition</item>
<item name="android:windowReturnTransition">@transition/detail_content_return_transition</item>
</pre></code>

詳情內容頁的進入和返回具有相同的transition方法。
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@integer/anim_duration_default">
// 1
<fade>
<targets>
<target android:targetId="@id/ordering" />
</targets>
</fade>
// 2
<slide android:slideEdge="bottom">
<targets>
<target android:targetId="@id/controls" />
</targets>
</slide>
</transitionSet>```
播放按鈕容器按targetId屬性指定的順序淡出。
通過targetId屬性指定,控制按鈕容器滑到底部。
為transition的變化率定義一個interpolator,可以是非線性的。
ProgressViewTransition是一個自定義的transition,使用ProgressView 來實現從水平進度到弧形進度的變換。
CoverViewTransition 是另一個自定義的transition,使用CoverView 實現方形封面到圓形軌道線封面的變換。
對其他的共享元素使用默認的transition。
<pre><code><item name="android:windowSharedElementReturnTransition">@transition/detail_shared_element_return_transition</item></pre></code>
在這個transition中,我們使用和上面的detail_shared_element_enter_transition幾乎相同的方法。不過對每部分添加了一些延時來配合UI上的效果。
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:duration="@integer/anim_duration_default"
android:interpolator="@android:interpolator/accelerate_quad">
// 1
<transitionSet>
<changeBounds />
<changeTransform />
<changeClipBounds />
<changeImageTransform />
<transition
class="com.sample.andremion.musicplayer.transition.ProgressViewTransition"
app:morph="1" />
<targets>
<target android:targetId="@id/progress" />
</targets>
</transitionSet>
// 2
<transitionSet android:startDelay="@integer/anim_duration_short">
<changeBounds />
<changeTransform />
<changeClipBounds />
<changeImageTransform />
<transition
class="com.sample.andremion.musicplayer.transition.CoverViewTransition"
app:shape="circle" />
<targets>
<target android:targetId="@id/cover" />
</targets>
</transitionSet>
// 3
<transitionSet android:startDelay="@integer/anim_duration_default">
<changeBounds />
<changeTransform />
<changeClipBounds />
<changeImageTransform />
</transitionSet>
</transitionSet>```
1. 使用相反的“變換”模式,從弧形進度到水平進度。
2. 使用相反的“變換”模式,從圓形封面到方形封面。
3. 對其他的共享元素使用默認的transition。
##最終結果
 coded by [André Mion](https://github.com/andremion)
](https://lh4.googleusercontent.com/kECd5Aw-Cr8ZllQaKX9wJf3TZTd5DmDArmBkFVpBbXtT9D4mGXgZlsSjSYH91oibOi9hv51jJ9XbjsMfcOZi8Zwrz_5pRs52ybKTRjAvCtDUk-RZGDFaRLNUMDd7YZLS6WXxmO0e)
最終的結果應該是這樣。當然,在最終的項目中可能會漏掉一些小細節,不過這都是小事情。
完整的項目可以在[https://github.com/andremion/Music-Player ](https://github.com/andremion/Music-Player)找到。
你也可以在下面的鏈接閱讀到更多關于Android上 meaningful motion的信息。
[Applying meaningful motion on Android](https://blog.prototypr.io/applying-meaningful-motion-on-android-a271a873bd78)
玩的愉快 ;)