InstaMaterial概念設計(3):feed卡片上的按鈕、評論按鈕

發表於2015-06-11

這篇文章是實現InstaMaterial的一部分,今天我們將仔細講解上篇文章中跳過的細節。也就是說我們實現的還是視訊中9-13秒這個時間段。

這是今天這篇文章完成之後的最終效果(棒棒糖以及棒棒糖之前):是youtube視訊,沒法看。暫時連不上。。。

初始化

沒有什麼大書特書的,我們只需為feed卡片元素中的按鈕(喜歡以及評論按鈕)加上圖示就可以了。這一步的程式碼提交在這裡this commit.完了之後,我們還是不忙著去實現新的東西(新的UI元素),還需要、、、

修正bug以及優化效能

是啊,即便是小如InstaMaterial 這樣的demo級應用,你也總能找到提升的空間。

Toolbar theme

首先我們遺漏了Toolbar的樣式,這就是為什麼menu按鈕(Toolbar左邊的按鈕)的按下顏色是預設的深色。如下:

(作者的目的是做的和視訊一模一樣,即便是顏色的深淺,個人認為沒必要這麼較真是吧)

下載按鈕(ToolBar右邊的按鈕)的按下顏色是淺色的,因為它是使用的帶selector的自定義view,selector的定義如下

menu_item_view.xml

但是這隻在Lollipop上有效果(這裡翻譯可能有誤,原文是This inconsistency appears only in Android Lollipop ),解決的辦法很簡單。只需在activity_comments.xml 和activity_main.xml的ToolBar控制元件中加上一行程式碼:

注:在toolbar中是這樣使用的:

這樣Toolbar上的所有元素都將有著繼承自Dark.ActionBar主題的樣式。

順便說下,如果你對android主題和樣式的定義感興趣,想知道他們的區別,這篇文章值得一讀-Styling Views on Android (Without Going Crazy).

按照上面的做了之後,menu的按下效果看起來就是這個樣子了(只在Lollipop 中):

RecyclerView 元素的預載入

另一個問題是在app啟動之後feed列表的滾動不太順滑。幾乎每次在滾到第二個卡片的時候都有卡頓。幸好,造成這個問題的原因簡單。RecyclerView (以及其他基於adapter的view,比如ListView、GridView等)使用了快取機制重用子view(簡而言之就是,系統只將螢幕可見範圍之內的元素儲存在記憶體中,在滾動的時候不斷的重用這些記憶體中已經存在的view,而不是新建view)。

這個機制在我們這裡會導致一個問題,啟動應用之後,在螢幕可見範圍內,我們只有一張卡片可見(估計作者的螢幕比較小),當我們滾動的時候,RecyclerView找不到可以重用的view了,它將建立一個新的,因此在滑動到第二個feed的時候就會有一定的延時,但是第二個feed之後的滾動是流暢的,因為這個時候RecyclerView已經有能重用的view了。

如何解決這個問題?

好在本例使用的是LinearLayoutManager ,因此很簡單。只需重寫getExtraLayoutSpace()方法。根據官方文件的描述getExtraLayoutSpace將返回LayoutManager應該預留的額外空間(顯示範圍之外,應該額外快取的空間)。

下面是實際效果:

在重寫getExtraLayoutSpace()之前

重寫之後:

Feed 卡片上的按鈕

下面就讓我們開始卡片上那些按鈕(目前只有喜歡和評論按鈕)的工作吧.從視訊中的效果來看是非常讚的,圓形擴散的selector

Android Lollipop中的Ripple效果

按鈕所使用的Selector無非就是棒棒糖中介紹的Ripple效果-一種水波擴散效果。已經有無數關於Ripple效果的文章,這裡就不贅述了,我只給出兩篇文章的連結:

我們專案中實現Ripple的方法是非常簡單的:

在feed_item.xml中將下面的drawable作為按鈕的背景

res/drawable-v21/btn_feed_action.xml

不要在L之前的裝置上使用ripples效果

我承諾過我將實現概念視訊中的所有效果,但是有時候,去做一件達不到預期的事情,還不如不做。在pre-21的裝置上實現ripple就是一件達不到預期的事情。

我當然知道可以使用諸如mimic ripple effect 這樣的庫來相容老裝置,但是沒有一個庫達到了該有的效果。它們需要新增額外的程式碼(比如新增額外的佈局來包裹),在Lollipop版本上無法使用原生的Ripple 效果,效能問題等等。

But why is so hard to copy Ripple effect into pre-21 Android?

但是為什麼在pre-21的裝置上覆制Ripple 效果會這麼難呢?

Ripple揭祕

在棒棒糖版本之前,整個UI都是在UI主執行緒中管理的。幾乎每個人都知道ANR對話方塊,NetworkOnMainThreadException,我們也是知道“不要將耗時操作放在UI執行緒中,僅僅在UI執行緒中顯示操作結果”這條黃金定律。一切都看似可行,除了那句“整個UI都是被UI主執行緒所管理的”。

隨著app的佈局日益複雜,UI需要更多的時間去繪製、測量。現在問題來了,如果我們的動畫執行到一半,而開啟了另外一個UI任務(比如為新的activity inflat佈局),但是隻有一個UI執行緒,那麼動畫將被停止。

Lollipop中所引入的Render執行緒將解決這個問題。Render執行緒通過將渲染分成兩部分來解決,簡單的來說就是:我們有一個被UI執行緒建立的動畫單元的列表,這些動畫將被甩到獨立的render執行緒當中。因此在執行開銷較大的UI操作的時候,動畫也能繼續下去。

這就是ripple效果的工作原理。他們是在render執行緒中執行的。所以不會被開啟新的activity這樣的操作打斷。

所以沒法在pre-21的安卓系統上完全實現ripple效果。

老版本上的相容“ripple”效果

因為不能在pre-21上實現ripple,那麼我們就實現類似的效果就可以了。我們建立了一個帶進入退出漸變的圓形的selector,儘管沒有ripple那麼花哨,但是看起來還是可以.

res/drawable/btn_feed_action.xml:

評論釋出按鈕

評論釋出按鈕非常有趣。正如你在視訊中看到的,按鈕可以在兩個狀態之間做簡單的動畫切換,並且點選的時候有ripple效果。

注:評論釋出按鈕對應的類是SendCommentButton.java,這一節中講解的內容都是在這個類中。結合程式碼更容易看明白。

關於這個按鈕SendCommentButton,我將用到下面幾個android元素:

ViewAnimator:作為SendCommentButton的基類,它有一個對我們非常有用的特性,可以設定子view切換時的進入和退出效果。如果你對ViewAnimator很陌生,其直接子類ViewFlipper, ViewSwitcher或間接子類ImageSwitcher, TextSwitcher應該很熟悉。

自定義view:包括xml以及inflate xml的程式碼

<merge>標籤消除冗餘的view

實現

好了,現在來開始實現,從動畫開始。實際上我們需要四個動畫,傳送狀態兩個(進入和退出),完成狀態兩個(進入和退出,不過是相反的方向):

傳送狀態:

滑出頂端

從頂端滑入

完成狀態:

從底部滑入

滑出底部

現在來實現button的佈局:

因為ViewAnimator本身是一個FrameLayout,因此需要使用<merge>標籤來減少了一層view。

最後,我們只有一個需求了,當button切換到完成狀態之後,隔兩秒它會自動切換回去。

程式碼很簡單:

init()方法(29行)將前面建立的佈局inflate給了ViewAnimator。順便可以去看下這篇文章proper Layout Inflation,裡面講解了些很可能被你忽略的細節。

為了防止在activity結束的時候按鈕的狀態還沒有切換回來,onDetachedFromWindow()去掉了回撥方法revertStateRunnable()。

其餘的都非常簡單,通過setInAnimation() and setOutAnimation()兩個方法來實現進入和退出動畫。

好了,剛剛我們完成了SendCommentButton 的實現。注:這部分最好根據文中提到的變數名方法名對照程式碼理解。

selector

和feed中的操作按鈕一樣,我們需要為SendCommentButton準備兩個selector。棒棒糖的裝置我們使用ripple效果。pre-21的裝置我們製造出標準的按下效果和陰影效果就可以了,就像在第一篇文章中對浮動操作按鈕的做法。

下面是兩種xml的程式碼:

res/drawable-v21/btn_send_comment.xml:

btn_send_comment_v21.xml hosted with ❤ by GitHub

res/drawable/btn_send_comment.xml:

btn_send_comment.xml hosted with ❤ by GitHub

錯誤振動提示

最後,我們將增加一個視訊中沒有出現的效果-當傳送評論時如果輸入框中沒有內容,則播放振動動畫。下面是效果圖:

Implementation is pretty simple – we have to create shake animation and custom CycleInterpolator for repeating this animation. Everything is in a few lines of code:

res/anim/shake_error.xml:

實現很簡單-建立一個振動動畫並使用自定義的CycleInterpolator插值器來重複播放這個動畫,只有幾行程式碼:

shake_error.xml hosted with ❤ by GitHub

res/anim/cycle_2.xml:

cycle_2.xml hosted with ❤ by GitHub

通過如下的程式碼來將這個動畫應用到按鈕中:

這就是今天所講的全部內容了,這裡是含有SendCommentButton類的最後一次提交:last commit 。下篇文章中我們將繼續實現概念視訊中的效果。

原始碼

討論中例子的原始碼在這裡:repository.

相關文章