Android仿微信通訊錄:懸停頭部分組列表
昨日,vivo與NBA在上海舉辦了戰略釋出會,宣佈vivo將成為NBA在中國唯一的手機市場官方合作伙伴。未來多年裡,雙方將在賽事、產品、服務以及品牌營銷等方面展開全方位戰略合作。
根據內部人士透露,vivo將會推出一系列NBA主題的深度定製手機以及周邊產品。同時,在未來多年中參與NBA國際系列賽中國站、NBA球迷答謝之夜等一系列品牌活動。此外,vivo還將推出“vivo校園籃球計劃”、“vivo走進賽場”等一系列品牌定製的NBA主題活動。
作者簡介本篇來自 張旭童 的投稿,分享瞭如何使用
張旭童 的部落格地址:
概述http://blog.csdn.net/zxt0601
效果如下,兩個ItemDecoration,一個實現懸停頭部分組列表功能,一個實現分割線:
網上關於實現帶懸停分組頭部的列表的方法有很多,像我看過有主席的自定義 ExpandListView 實現的,也看過有人用一個額外的父佈局裡面套 RecyclerView/ListView + 一個頭部View
對於以上解決方案,有以下幾點個人覺得不好的地方:
1. 現在 RecyclerView 是主流。
2. 在 RecyclerView 外套一個父佈局總歸是增加布局層級,容易overdraw,顯得不夠優雅。
3. item佈局 實現帶這種分類頭部的方法有兩種,一種是把分類頭部當做一種itemViewtype(麻煩),另一種是每個 Item佈局 都包含了分類頭部的佈局,程式碼里根據postion 等資訊動態 Visible,Gone頭部(佈局冗餘,item效率降低)。
況且Google為我們提供了 ItemDecoration
而且更重要的是,ItemDecoration 出來這麼久了,你還不用它?
本文就利用 ItemDecoration 打造 分組列表,並配有懸停頭部功能。
亮點預覽:新增多個ItemDecoration、它們的執行順序、ItemDecoration方法執行順序、ItemDecoration和RecyclerView的繪製順序。
使用ItemDecoration用法:為 RecyclerViewPool 新增 一個或多個 ItemDecoration:
為 RecyclerView 新增 ItemDecoration 只要這麼一句 addItemDecoration()。
它有兩個同名過載方法:
addItemDecoration(ItemDecoration decor) 常用,(按照add順序,依次渲染ItemDecoration)
addItemDecoration(ItemDecoration decor, int index) add一個ItemDecoration,併為它指定順序
上來就高能,別的講解 RecyclerView 的文章一般都是對 ItemDecoration 一筆帶過,用的Demo 一般也都是官方的 DividerItemDecoration類,更別提還新增多個 ItemDecoration了。其實我也是寫Demo的時候才發現這個方法,點進去查看了一下原始碼:
老套路:我們最常用的單引數方法 內部呼叫了雙引數方法,並把index 傳入-1。
我們 add 的 ItemDecoration 都儲存在 RecyclerView 類的 mItemDecorations 變數裡, 這個變數就是一個ArrayList,定義如下:
private final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
ItemDecoratuon方法介紹
常用(全部)方法,按照在 RecyclerView 中它們被呼叫的順序排列:
這個三個方法也是繼承一個 ItemDecoration 必須實現的三個方法。(其實 ItemDecoration 裡除了@Deprecated 的方法 也就它們三了)
方法一的編寫
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
我們需要利用 parent 和 state 變數,來獲取需要的輔助資訊,例如 postion, 最終呼叫 outRect.set(int left, int top, int right, int bottom)方法,設定四個方向上需要為 ItemView 設定 padding 的值。
下圖我覺得很經典:摘自(http://blog.piasy.com/2016/03/26/Insight-Android-RecyclerView-ItemDecoration/?utm_source=tuicool&utm_medium=referral):
本文的 實體bean 如下編寫:
getItemOffsets 方法 如下:
通過 parent 獲取 postion 資訊,通過 postion 拿到資料裡的每個bean裡的分類,因為資料集已經有序,如果與前一個分類不一樣,說明是一個新的分類,則需要繪製頭部outRect.set(0, mTitleHeight, 0, 0),否則不需要 outRect.set(0, 0, 0, 0)。
方法二的編寫
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state)
我們需要利用 parent 和 state 變數,來獲取需要的輔助資訊,例如繪製的上下左右,childCount, childView 等。最終利用 Canvas c 呼叫 Canvas 的方法來繪製出我們想要的UI。會自定義View就會寫本方法。
onDraw 繪製出的內容是在 ItemView 下層,雖然它可以繪製超出 getItemOffsets() 裡的Rect區域,但是超出區域最終不會顯示,但被 ItemView 覆蓋的區域會產生 OverDraw。
本文如下編寫:通過parent獲取繪製UI的 left 和 right 以及 childCount, 遍歷 childView,根據 childView 的 postion,和方法一中的判斷方法一樣,來決定是否繪製分類Title區域。
分類繪製 title 的方法就是 自定義View 的套路,根據確定的上下左右範圍先 drawRect 繪製一個背景,然後 drawText 繪製文字。
寫完方法1、2,就已經完成了分類列表title的繪製,方法3實現頂部懸停title效果。
方法三的編寫
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state)
和 onDraw() 方法類似, 我們需要利用 parent 和 state 變數,來獲取需要的輔助資訊,例如繪製的上下左右,position, childView等。最終利用 Canvas c 呼叫 Canvas 的方法來繪製出我們想要的UI。同樣是會自定義View就會寫本方法。
onDrawOver 繪製出的內容是在 RecyclerView 的最上層,會遮擋住 ItemView,So天生自帶懸停效果,用來繪製懸停View再好不過。
本文如下編寫:首先通過 parent 獲取 LayoutManager(由於懸停分組列表的特殊性,寫死了是 LinearLayoutManger),然後獲取當前第一個可見 itemView 以及 postion,以及它所屬的分類 title(tag),然後繪製懸停View的背景和文字(tag),可參考方法2裡的書寫,大同小異。
至此,我們的 帶懸停頭部的分組列表 的 ItemDecoration 就編寫完畢了。
一些ItemDecoration補充一.多個ItemDecoration,以及它們的繪製順序。
就像上面用法提到的,可以為一個 RecyclerView 新增 多個ItemDecoration,那麼 多個ItemDecoration 的繪製順序是什麼呢?
多個ItemDecoration 最終是儲存在 RecyclerView 裡的 mItemDecorations(ArrayList) 變數中,那我們就去 RecyclerView 的原始碼裡搜一搜,看看哪些地方用到了 mItemDecorations。
發現在 draw() 和 onDraw() 方法裡:按照在 mItemDecorations 裡的 postion順序,依次呼叫了 每個 ItemDecoration 的 onDrawOver 和 onDraw 方法。所以 後新增的ItemDecoration,如果和 前面的ItemDecoration 的繪製區域有重合的地方,會遮蓋住前面的ItemDecoration(OverDraw)。
二. ItemDecoration和RecyclerView的Item的繪製順序
在介紹ItemDecoration的三個方法時,我們提到過結論:
ItemDecoration 的 onDraw 最先呼叫,繪製在最底層;
其上再繪製 ItemView 中間層;
再上呼叫 ItemDecoration 的 onDrawOver,繪製在最上層。
理由: 由上面程式碼可見, RecyclerView 的 draw() 方法中,在 super.draw(c) 方法呼叫完後,才呼叫 mItemDecorations.get(i).onDrawOver(c, this, mState);
而 super.draw(c) 方法就是直接呼叫 View 的 public void draw(Canvas canvas) 方法(如下圖所示) 其中又先呼叫了 View(RecyclerView)的 onDraw() 方法;
在 RecyclerView 的 onDraw() 方法中,會呼叫 mItemDecorations.get(i).onDraw(c, this, mState);
所以onDraw最先呼叫,繪製在最底層
後呼叫了 View(ViewGroup)的 dispatchDraw(canvas) 方法,會執行 drawChild(Canvas canvas, View child, long drawingTime) 方法,繪製每個 itemView。
所以ItemView繪製在中間層
最後 super.draw(c) 走完,呼叫 mItemDecorations.get(i).onDrawOver(c, this, mState);
所以 再呼叫 ItemDecoration 的 onDrawOver,繪製在最上層。(從方法名字也可以看出哈)
View 的 draw() 方法如下:
總結RecyclerView相關的各個類,個個是寶,每一次探索都覺得如獲至寶, 感覺利用ItemDecoration可以幹很多事,可惜ItemDecoration貌似不能接受到使用者的點選事件~要不我右側導航欄都想用ItemDecoration實現了。
關於可以 add 多個ItemDecoration 這一點,想了一下,覺得很精妙,這是一種很好的設計思想,多個ItemDecoration 各司其職,如本文,採用官方 ItemDecoration 作分割線,自己又寫一個 ItemDecoration 作 分類title 和 分類title 相關的 懸停title。用時根據需要,選擇任意數量的“裝飾品” ItemDecoration,來豐富你的 RecyclerView。可能我的low常規思想還是一個XXX類,使用時如果擴充功能,需要extends and code~但這樣不同的功能就太耦合了,不利於複用。畢竟 “組合大於繼承”。
點選最後 閱讀原文 檢視原始碼。
如果你有好的技術文章想和大家分享,歡迎向我的公眾號投稿,投稿具體細節請在公眾號主頁點選“投稿”選單檢視。
歡迎長按下圖 -> 識別圖中二維碼或者掃一掃關注我的公眾號:
覺得有幫助就贊一下唄
人讚賞