很久沒有寫博客了,主要仍是任務過多哈。在開發的過程中,也記錄了不少東西,可是技術這個事吧,其實,時效性真的事很是強……就好比說,你昨天還津津樂道的一個難點解決方案,你過個幾天再回過頭去看它,就會有一種莫名的「輕視感」(不知道有沒有這個說法,反正你們本身體會吧……),以爲它也很少如此嘛。而後慢慢的就不知道分享什麼了。也所以,趁着熱度還在,趕忙跟你們分享一下剛剛完成一個基於CoordinatorLayout的比較複雜的交互邏輯。html
剛看到這個交互的時候,我也嘗試了蠻多搜索的,可是很遺憾,結果就是隻能本身搞啦~我們先看一下效果圖哈,先一飽眼福,再進行深究。另外也附上Github代碼倉庫,方便你們實際操做:android
https://github.com/wytings/SpecialTabIndicatorgit
看着效果仍是蠻炫的,可是怎麼作呢?下面慢慢給你們分析一下,我在開發這個交互的整個過程。主要分爲兩大部分:1、理論分析;2、技術點概況;3、技術實現細節github
首先,一看到這個交互圖。我首先想到是以前寫的一篇:功能分解——Android下畫分時圖與k線圖有感。咱們要進行的第一步就是分解這個交互。緩存
一、頂部的TitleBar —— 背景從透明變黑、左邊返回按鈕白變藍、右邊loading progress 透明度0到1以及旋轉;app
二、頭部底層佈局——能夠切換的圖片,有漸變色、有切換動畫;less
三、頭部下面的指示器——相似於普通的Indicator,可是selected和unselected的顏色會變化;ide
四、底部ViewPager——ViewPager就沒有什麼說的了。佈局
以上就是咱們分拆的4個大布局類,咱們姑且把其序號當編號用,方便描述。分拆完了之後,咱們再回過頭去看看,他們各自的Layout行爲表現:post
1號佈局位置固定不變,顏色變化;
2號佈局位置固定不變,顏色和高度變化;
3號佈局位置、顏色變化;
4號佈局位置變化;
而後就會發現、全部的變化都在迴繞着2號佈局的高度而發生改變。咱們只須要讓其餘佈局都去監聽2號佈局的高度變化,便可進行響應的改變了(固然這裏面還有滑動衝突的解決)。有了這個思路後,即使本身寫一套是否是感受也明朗好多了?可是,使人更加雀躍的是,Android裏面有一個佈局是徹底能夠勝任這個事的,就是CoordinatorLayout。
儘管咱們知道總體上是使用CoordinatorLayout來處理佈局關係,但其實仍是涉及了不少動畫的改變,圖標的變化等。咱們要抽出一個個技術點來,纔好進行具體的實施。接下來,咱們要作的事就是這個。
2.一、背景設置漸變色
純色背景的設置仍是比較簡單,漸變色的設置則須要進行的必定的處理。這裏我想到的是 GradientDrawable。經過這個類,咱們能夠進行漸變背景的設置。這裏比較建議代碼操做而不是經過XML去操做,由於他們漸變邏輯是同樣的,若是經過XM來進行,則需創建的XML文件較多。
2.二、顏色漸變切換
這裏涉及到顏色切換的漸變,A顏色->B 顏色的過程得是逐漸改變而不是突變。一般這種邏輯主要涉及在動畫處理。若是要單獨處理則能夠抽出其中的一個關鍵類:ArgbEvaluator。經過這個類,咱們能夠計算出兩種顏色發生改變的中間值這個過程可調節的參數爲0到1。
2.三、PNG圖標顏色漸變
咱們看到返回箭頭實際上是一個PNG圖標,代碼裏面讀取後就是一個Drawable,因此咱們要對Drawable進行漸變處理。這裏就涉及到一個叫TintColor的東西,經過設置它能夠改變在繪製Drawable的時候的顏色。好比上面的返回圖標實際上是白色的,可是在繪製這個圖標的時候,是能夠改變顏色的。另外須要注意的是Drawable默認是系統複用的,因此須要進行mutate()一下,避免你在修改他屬性的時候影響其餘場景的使用。
2.四、頂部圖片切換動畫
這裏沒有使用ViewPager,是由於ViewPager切換動畫比較侷限。雖然ViewPager能夠自定義PageTransformer進行自定義動畫切換界面,可是依然很難知足上圖的交互需求,尤爲是要進行跨Tab切換的時候。那咱們使用什麼呢?我選擇了ViewAnimationor。由於它能夠自定義畫面的進入仍是出去,可是因爲它是單純爲了顯示,因此要本身添加手勢的監聽來支持左右切換。
2.五、整個佈局的擺放
這個就涉及到了CoordinatorLayout的用法問題了。這個控件對佈局的操做都依賴於CoordinatorLayout.Behavior,這個類是咱們這個交互最核心的類。經過對子佈局設置Behavior,能夠決定其怎麼展現。
上面咱們已經大概說明了一些依賴的技術點。若是你們對以上所說的點有不熟悉的話,最好仍是先去看看相關資料。這樣對於接下來要說的,會更省心一些。另外,主要是挑幾個要點講不可能無腦貼代碼。具體的代碼實現,你們能夠把Github上clone下來,本身慢慢看。
首先,咱們來看一下 GradientDrawable 這個類。
/** * Create a new gradient drawable given an orientation and an array * of colors for the gradient. */
public GradientDrawable(Orientation orientation, @ColorInt int[] colors) { this(new GradientState(orientation, colors), null); }
經過構造方法,咱們能夠看出來,漸變色的實現仍是比較簡單的。
GradientDrawable gradient = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, new int[]{startColor, endColor}); view.setBackground(gradient);
圖標的顏色漸變切換就是支持取出Drawable,而後進行tint操做。
Drawable wrappedDrawable = DrawableCompat.wrap(imageBack.getDrawable()).mutate(); ColorStateList tint = ColorStateList.valueOf(currentColor); DrawableCompat.setTintList(wrappedDrawable, tint);
咱們來看一下上面的操做,首先取出返回按鈕的Drawable,記得要mutate(),顯性聲明這個Drawable再也不與其餘地方進行分享,單獨持有。
第二步就是進行ColorStateList的建立,你們別看每次valueOf一下就會建立一個新的,裏面是有緩存機制的。而後直接更新其TintList。
最後咱們再來看看大頭的 CoordinatorLayout.Behavior。有兩個方法 layoutDependsOn、onDependentViewChanged是影響其佈局位置的。經過註釋,咱們也能夠發現,第一個就是要聲明,該Behavior附屬的View依賴於哪個View,在CoordinatorLayout遍歷整個View節點的時候會不斷回調,直到return true,告訴它這個view就是個人依賴。那麼日後,這個Behavior裏面全部方法中的但凡是有dependency參數的都是這個return true聲明的View。有點繞是吧…
另一個方法onDependentViewChanged就是在你聲明依賴後,若是依賴的View發生位置或大小的變化時,就會經過這個回調進行通知,而後就能夠進行相關View改變了。
/** * Determine whether the supplied child view has another specific sibling view as a * layout dependency. * * <p>This method will be called at least once in response to a layout request. If it * returns true for a given child and dependency view pair, the parent CoordinatorLayout * will:</p> * <ol> * <li>Always lay out this child after the dependent child is laid out, regardless * of child order.</li> * <li>Call {@link #onDependentViewChanged} when the dependency view's layout or * position changes.</li> * </ol> * * @param parent the parent view of the given child * @param child the child view to test * @param dependency the proposed dependency of child * @return true if child's layout depends on the proposed dependency's layout, * false otherwise * * @see #onDependentViewChanged(CoordinatorLayout, android.view.View, android.view.View) */
public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) { return false; } /** * Respond to a change in a child's dependent view * * <p>This method is called whenever a dependent view changes in size or position outside * of the standard layout flow. A Behavior may use this method to appropriately update * the child view in response.</p> * * <p>A view's dependency is determined by * {@link #layoutDependsOn(CoordinatorLayout, android.view.View, android.view.View)} or * if {@code child} has set another view as it's anchor.</p> * * <p>Note that if a Behavior changes the layout of a child via this method, it should * also be able to reconstruct the correct position in * {@link #onLayoutChild(CoordinatorLayout, android.view.View, int) onLayoutChild}. * <code>onDependentViewChanged</code> will not be called during normal layout since * the layout of each child view will always happen in dependency order.</p> * * <p>If the Behavior changes the child view's size or position, it should return true. * The default implementation returns false.</p> * * @param parent the parent view of the given child * @param child the child view to manipulate * @param dependency the dependent view that changed * @return true if the Behavior changed the child view's size or position, false otherwise */
public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) { return false; }
最後一個就是,滑動衝突的處理,固然也是在Behavior裏面操做的。我就不截圖了。咱們直接看具體的方法名:onStartNestedScroll、onNestedScrollAccepted、onNestedPreScroll、onNestedScroll、onNestedPreFling、onStopNestedScroll
。是否是看着有點恐怖…是的,剛開始不熟悉的話會很痛苦,理解了就行了。注意這個方法名的循序就是一個滑動操做的通用過程。
看看方法名,基本就知道是幹嗎的了吧。須要注意的兩點是:
一、在onStarrtNestedScroll必須返回true,後續的方法纔會收到剩餘的Touch事件。而後就能夠慢慢處理了。
二、若是在嵌套滑動的時候,打算提早處理,若是還有沒消耗完的距離,要記得告訴外面的View,避免生硬的滑動。就是onNestedPreScroll中的consumed.
public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull ViewPager child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type)
其實,整個難點基本就沒了…剩下的都是實打實的細節調整。另外,這篇不是入門級解說哈,像裏面涉及的動畫切換就不可能再分拆出來講了。不然,就光光那個ViewPager的Indicator均可以分拆出一個項目來進行說明了……
這個還請那些對CoordinatorLayout不太瞭解的同窗多多包含,本身多看點源碼,跑一遍。看到手機上真實的效果後,會激起很高的學習熱情的。
你們加油!