AndroidTv Home界面实现原理(二)——Leanback 库的主页卡位缩放动画源码解析
本篇文章已授權(quán)微信公眾號 dasu_Android(大蘇)獨(dú)家發(fā)布
先看個(gè)效果圖:
上一篇中,我們留了問題,在 Tv Home 界面這種很常見聚焦卡位放大動畫效果,我們這一篇就來看看 Leanback 庫是怎么實(shí)現(xiàn)的。
如果要我們自己實(shí)現(xiàn)的話,思路應(yīng)該不難,就是寫個(gè)放大、縮小動畫,然后在卡位獲得焦點(diǎn)時(shí)應(yīng)用放大動畫,失去焦點(diǎn)時(shí)應(yīng)用縮小動畫,所以關(guān)鍵點(diǎn)只是在于如何進(jìn)行封裝。那下面就來學(xué)學(xué) Google Leanback 庫的 ItemView 縮放動畫的實(shí)現(xiàn)思路。
源碼分析
看源碼時(shí),我習(xí)慣帶著目的性地去閱讀,這樣只要專注于理解跟目的相關(guān)的代碼即可,不用每行代碼地去分析,畢竟好多代碼目前能力有限,還啃不透。
那么,我們這次閱讀源碼的目的就是要搞清楚:卡位獲得焦點(diǎn)時(shí)放大、縮小動畫是如何實(shí)現(xiàn)的?
閱讀源碼時(shí)經(jīng)常會碰到一個(gè)問題,那就是該從哪入手,從哪開始看?
這就是為什么我習(xí)慣帶著目的去閱讀,因?yàn)槲覀兛梢詮哪康姆治?#xff0c;猜測我們需要的代碼應(yīng)該在哪里,然后找到我們該從哪里閱讀,再一步步的去分析。
比如我們這次的任務(wù),我們該從哪里入手閱讀源碼呢?首先,你得先了解一下 Leanback 庫的基本使用,這就是為什么我第一篇博客先簡單介紹了 Leanback 庫的使用。在上一篇博客里,可以看到,我們跟 Leanback 庫打交道的也就是下面這幾個(gè)類:
- ArrayObjectAdapter:作用類似于 List,裝填著整個(gè)頁面的數(shù)據(jù),頁面數(shù)據(jù)其實(shí)是分兩級,以行為單位和以每一行中的 Item 為單位,所以整個(gè)頁面有一個(gè) ArrayObjectAdapter(mRowsAdapter) 對象,它由許多行數(shù)據(jù) ArrayObjectAdapter(rowAdapter) 對象組成,每行數(shù)據(jù) rowAdapter 由許多 Item 組成。初始化一個(gè) ArrayObjectAdapter 對象時(shí),必須提供一個(gè) Presenter 對象與它關(guān)聯(lián)。
- ListRowPresenter:Leanback 庫中的 Presenter 作用都有些類似于 RecyclerView.Adapter,用于創(chuàng)建 ItemView 以及將數(shù)據(jù)綁定到 ItemView 上。
- ListRow:可以理解成一個(gè) Mode,也就是把每一行抽象封裝成一個(gè) ListRow
- BrowerFragment:用來展示可左右上下滑動的視頻列表界面,Leanback 已高度封裝,我們只需提供一個(gè)頁面的 ArrayObjectAdapter(mRowsAdapter) 對象,通過 setAdapter() 將數(shù)據(jù)設(shè)置進(jìn)去,Leanback 會自動根據(jù) ArrayObjectAdapter 里的數(shù)據(jù)以及和它關(guān)聯(lián)的 Presenter 將界面顯示出來。
既然我們跟 Leanback 打交道只有這么幾點(diǎn),那么切入點(diǎn)應(yīng)該就在這些,畢竟我們對 Leanback 并不熟,那么只能從我們接觸到的地方來著手。
那么,再來想想,既然是要實(shí)現(xiàn)卡位獲得焦點(diǎn)和失去焦點(diǎn)時(shí)放大和縮小動畫,那么肯定是需要監(jiān)聽 ItemView 的焦點(diǎn)變化,對吧?那我們通常是怎么做的,無外乎就是在 RecyclerView.Adapter 里的 onCreateViewHolder() 或 onBindViewHolder() 里監(jiān)聽 ItemView 的焦點(diǎn)變化吧。
既然方向有了,那么就是要尋找 Leanback Home 界面對應(yīng)的 RecyclerView.Adapter 是由哪個(gè)類實(shí)現(xiàn)的吧。我們也知道了在 Leanback 中 Presenter 的作用就是類似于 RecyclerView.Adapter,那么我們就先到 Presenter 里看一下。
ListRowPresenter 繼承自 RowPresenter 繼承自 Presenter,那么我們通過 Android Studio 跳到 Presenter 里看看。
Presenter 是個(gè)抽象類,有三個(gè)抽象方法, onCreateViewHolder()、onBindViewHolder()、onUnbindViewHolder(),跟 RecyclerView.Adapter 很像吧。根據(jù)我們之前的分析, ItemView 焦點(diǎn)的監(jiān)聽通常是在 onCreateViewHolder() 或 onBindViewHolder() 里實(shí)現(xiàn)的,那么我們就去它的實(shí)現(xiàn)類 ListRowPresenter 里看一看。
ListRowPresenter 里找不到這三個(gè)方法的實(shí)現(xiàn),那么就是由它的父類 RowPresenter 實(shí)現(xiàn)了,我們繼續(xù)通過 AS 跳到 RowPresenter 里看看。
onCreateViewHolder() 里的代碼我們不用去理解,當(dāng)然你有時(shí)間有能力也可以,但現(xiàn)在主要是想搞懂它的卡位縮放動畫實(shí)現(xiàn),所以我們只要看有沒有跟焦點(diǎn)監(jiān)聽相關(guān)的代碼即可。很顯然,這里面并沒有找到,里面調(diào)用了幾個(gè)方法,有些方法一看就知道作用是創(chuàng)建某個(gè)對象的,你們也可以點(diǎn)進(jìn)去看看,這里我們著重看一下 initializeRowViewHolder() 這個(gè)方法。
好像也沒找到跟焦點(diǎn)監(jiān)聽的相關(guān)代碼,但是左邊有個(gè)標(biāo)志,說明子類 ListRowPresenter 有復(fù)寫這個(gè)方法,那么代碼運(yùn)行時(shí)實(shí)際上是調(diào)用的之類的方法,那么我們就點(diǎn)進(jìn)去看看。
代碼很多,截圖也沒截完,但是我們發(fā)現(xiàn)了一個(gè)關(guān)鍵,找到了一個(gè)看名字就覺得跟焦點(diǎn)有關(guān)的方法 FocusHighlightHelper.setupBrowseItemFocusHighlight(),那么到底是不是呢?我們繼續(xù)點(diǎn)擊去看一下。
這個(gè)方法的介紹大意就是說設(shè)置每行的 ItemView 即卡位獲得焦點(diǎn)時(shí)的行為,這不就是指卡位的縮放動畫嘛,看來我們找到了。看代碼,是調(diào)用了 ItemBridgeAdapter 的 setFocusHighlight() 方法,繼續(xù)跟進(jìn)看一下。
方法就只是設(shè)置了 mFocusHighlight 變量的值,而 ItemBridgeAdapter 是繼承 RecyclerView.Adapter 的,看來卡位的焦點(diǎn)監(jiān)聽就是在這里實(shí)現(xiàn)了。看一下 onCreateViewHolder() 方法就知道是不是了。
presenterView 其實(shí)就是在 Presenter 里創(chuàng)建出來的 ItemView,所以這里其實(shí)就是對卡位設(shè)置焦點(diǎn)的變化監(jiān)聽,viewHolder.mFocusChangeListener 應(yīng)該是 View.OnFocusChangeListener 的對象,我們看一下。
mFocusChangeListener 初始化通過實(shí)例化一個(gè)類對象賦值,那么這個(gè)類應(yīng)該就是實(shí)現(xiàn) View.OnFocusChangeListener 接口的,我們繼續(xù)看一下。
這下清楚了吧,焦點(diǎn)發(fā)生變化時(shí),會去調(diào)用 mFcousHightlight 的 onItemFocused() 方法。而 mFcousHightlight 是之前 FocusHighlightHelper.setupBrowseItemFocusHighlight() 里調(diào)用了 ItemBridgeAdapter 的 setFocusHighlight() 方法傳進(jìn)來的,我們再看看它傳進(jìn)來的是什么。
看一下 BrowseItemFocusHighlight 這個(gè)類做了什么。
所以,在 ItemBridgeAdapter 里注冊了焦點(diǎn)變化監(jiān)聽,當(dāng)焦點(diǎn)變化時(shí),通知 mFcousHightlight 執(zhí)行 onItemFocused() 方法,而 mFcousHightlight 是 BrowseItemFocusHighlight 類的實(shí)例,所以實(shí)際上是來執(zhí)行上圖里的邏輯。看代碼也很容易明白,設(shè)置 ItemView 的選中狀態(tài),并且去運(yùn)行一個(gè)焦點(diǎn)動畫,那么卡位的縮放動畫應(yīng)該就是在這里實(shí)現(xiàn)了。繼續(xù)看一下是不是。
該方法其實(shí)就是創(chuàng)建一個(gè)動畫對象,如果該對象有緩存的話,那么就從緩存中取出,沒有的話,就 new 一個(gè),這種緩存的思想很值得學(xué)習(xí)。
該類就是實(shí)現(xiàn)了縮放的動畫效果了,通過實(shí)現(xiàn) TimeAnimator.TimeListener 接口來實(shí)現(xiàn)的屬性動畫,當(dāng)然縮放動畫也可以用其他方式實(shí)現(xiàn),無非就是對 View 進(jìn)行放大、縮小而已,這里就不具體去分析了,感興趣的可以自己來這里看看 Google 是如何實(shí)現(xiàn)縮放動畫的,后期有時(shí)間的話我可以再來分析一下這個(gè)類。
好了,到這里基本就分析完了,Leanback 庫關(guān)于卡位的縮放動畫的實(shí)現(xiàn),從我們要從哪里著手開始閱讀源碼到找到焦點(diǎn)監(jiān)聽實(shí)現(xiàn)的相關(guān)代碼到動畫實(shí)現(xiàn)的代碼整個(gè)過程基本就是這樣。以后大家在想看源碼的某個(gè)功能是如何實(shí)現(xiàn)時(shí),可以參考這種思路來進(jìn)行分析,一步步的去跟進(jìn),只找我們目標(biāo)相關(guān)的代碼,這樣可以不至于被整個(gè)源碼的復(fù)雜性混亂掉。
最后,我想再總結(jié)一下上面的過程。
總結(jié)
- 卡位縮放動畫的實(shí)現(xiàn)在類 FocusHighlightHelper 的內(nèi)部類 FocusAnimator 里實(shí)現(xiàn)。
- 縮放動畫跟 ItemView 的綁定過程:
RowPresenter#onCreateViewHoler()
-> RowPresenter#initializeRowViewHolder()
-> ListRowPresenter#initializeRowViewHolder()
-> FocusHighlightHelper#setupBrowseItemFocusHighlight()
-> ItemBridgeAdapter#setFocusHighlight() - 簡單點(diǎn)說就是,當(dāng)每一行的 View 要?jiǎng)?chuàng)建時(shí),會注冊一個(gè)焦點(diǎn)監(jiān)聽器,該行里的 ItemView 焦點(diǎn)發(fā)生變化時(shí)會從 ItemViwe 的 Tag 里取出縮放動畫對象,如果沒有則 new 一個(gè),然后應(yīng)用縮放動畫。
最近剛開通了公眾號,想激勵(lì)自己堅(jiān)持寫作下去,初期主要分享原創(chuàng)的Android或Android-Tv方面的小知識,感興趣的可以點(diǎn)一波關(guān)注,謝謝支持~~
轉(zhuǎn)載于:https://www.cnblogs.com/dasusu/p/7475419.html
總結(jié)
以上是生活随笔為你收集整理的AndroidTv Home界面实现原理(二)——Leanback 库的主页卡位缩放动画源码解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python学习笔记-day1(whil
- 下一篇: Android ---------高德卫