1. 程式人生 > >Android仿微信通訊錄:懸停頭部分組列表

Android仿微信通訊錄:懸停頭部分組列表

640?wx_fmt=png

今日科技快訊

昨日,vivo與NBA在上海舉辦了戰略釋出會,宣佈vivo將成為NBA在中國唯一的手機市場官方合作伙伴。未來多年裡,雙方將在賽事、產品、服務以及品牌營銷等方面展開全方位戰略合作。

根據內部人士透露,vivo將會推出一系列NBA主題的深度定製手機以及周邊產品。同時,在未來多年中參與NBA國際系列賽中國站、NBA球迷答謝之夜等一系列品牌活動。此外,vivo還將推出“vivo校園籃球計劃”、“vivo走進賽場”等一系列品牌定製的NBA主題活動。

作者簡介

本篇來自 張旭童 的投稿,分享瞭如何使用

ItemDecoration 實現 懸停頭部分組列表功能,這種實現方式還是很簡便的,希望能幫助有需要的朋友。

張旭童 的部落格地址:

http://blog.csdn.net/zxt0601

概述

效果如下,兩個ItemDecoration,一個實現懸停頭部分組列表功能,一個實現分割線:

640?wx_fmt=gif

網上關於實現帶懸停分組頭部的列表的方法有很多,像我看過有主席的自定義 ExpandListView 實現的,也看過有人用一個額外的父佈局裡面套 RecyclerView/ListView + 一個頭部View

(位置固定在父佈局上方)實現的。

對於以上解決方案,有以下幾點個人覺得不好的地方: 

1. 現在 RecyclerView 是主流。

2.RecyclerView 外套一個父佈局總歸是增加布局層級,容易overdraw,顯得不夠優雅。 

3. item佈局 實現帶這種分類頭部的方法有兩種,一種是把分類頭部當做一種itemViewtype(麻煩),另一種是每個 Item佈局 都包含了分類頭部的佈局,程式碼里根據postion 等資訊動態 Visible,Gone頭部(佈局冗餘,item效率降低)。

況且Google為我們提供了 ItemDecoration

,它本身就是用來修飾 RecyclerView 裡的Item 的,它的 getItemOffsets()onDraw() 方法用於為Item分類頭部留出空間和繪製(解決缺點3),它的 onDrawOver() 方法用於繪製懸停的頭部View(解決缺點2)。 

而且更重要的是,ItemDecoration 出來這麼久了,你還不用它?

本文就利用 ItemDecoration 打造 分組列表,並配有懸停頭部功能。

亮點預覽:新增多個ItemDecoration、它們的執行順序、ItemDecoration方法執行順序、ItemDecoration和RecyclerView的繪製順序

使用ItemDecoration

用法:為 RecyclerViewPool 新增 一個或多個 ItemDecoration

640?wx_fmt=png

RecyclerView 新增 ItemDecoration 只要這麼一句 addItemDecoration()

它有兩個同名過載方法: 

  • addItemDecoration(ItemDecoration decor) 常用,(按照add順序,依次渲染ItemDecoration)

  • addItemDecoration(ItemDecoration decor, int index) add一個ItemDecoration,併為它指定順序

上來就高能,別的講解 RecyclerView 的文章一般都是對 ItemDecoration 一筆帶過,用的Demo 一般也都是官方的 DividerItemDecoration類,更別提還新增多個 ItemDecoration了。其實我也是寫Demo的時候才發現這個方法,點進去查看了一下原始碼:

640?wx_fmt=png

老套路:我們最常用的單引數方法 內部呼叫了雙引數方法,並把index 傳入-1

我們 add 的 ItemDecoration 都儲存在 RecyclerView 類的 mItemDecorations 變數裡, 這個變數就是一個ArrayList,定義如下:

private final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
ItemDecoratuon方法介紹

常用(全部)方法,按照在 RecyclerView 中它們被呼叫的順序排列:

640?wx_fmt=png

這個三個方法也是繼承一個 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):

640?wx_fmt=png

本文的 實體bean 如下編寫:

640?wx_fmt=png

getItemOffsets 方法 如下:

通過 parent 獲取 postion 資訊,通過 postion 拿到資料裡的每個bean裡的分類,因為資料集已經有序,如果與前一個分類不一樣,說明是一個新的分類,則需要繪製頭部outRect.set(0, mTitleHeight, 0, 0),否則不需要 outRect.set(0, 0, 0, 0)。

640?wx_fmt=png

方法二的編寫

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 繪製文字。

640?wx_fmt=png

寫完方法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裡的書寫,大同小異。

640?wx_fmt=png

至此,我們的 帶懸停頭部的分組列表 ItemDecoration 就編寫完畢了。

一些ItemDecoration補充

一.多個ItemDecoration,以及它們的繪製順序。

就像上面用法提到的,可以為一個 RecyclerView 新增 多個ItemDecoration,那麼 多個ItemDecoration 的繪製順序是什麼呢?

多個ItemDecoration 最終是儲存在 RecyclerView 裡的 mItemDecorations(ArrayList) 變數中,那我們就去 RecyclerView 的原始碼裡搜一搜,看看哪些地方用到了 mItemDecorations

發現在 draw() onDraw() 方法裡:按照在 mItemDecorations 裡的 postion順序,依次呼叫了 每個 ItemDecoration onDrawOver onDraw 方法。所以 後新增的ItemDecoration,如果和 前面的ItemDecoration 的繪製區域有重合的地方,會遮蓋住前面的ItemDecoration(OverDraw)。

640?wx_fmt=png

二. 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() 方法如下:

640?wx_fmt=png

總結

RecyclerView相關的各個類,個個是寶,每一次探索都覺得如獲至寶, 感覺利用ItemDecoration可以幹很多事,可惜ItemDecoration貌似不能接受到使用者的點選事件~要不我右側導航欄都想用ItemDecoration實現了。

關於可以 add 多個ItemDecoration 這一點,想了一下,覺得很精妙,這是一種很好的設計思想,多個ItemDecoration 各司其職,如本文,採用官方 ItemDecoration 作分割線,自己又寫一個 ItemDecoration 作 分類title 和 分類title 相關的 懸停title。用時根據需要,選擇任意數量的“裝飾品” ItemDecoration,來豐富你的 RecyclerView。可能我的low常規思想還是一個XXX類,使用時如果擴充功能,需要extends and code~但這樣不同的功能就太耦合了,不利於複用。畢竟 “組合大於繼承”。

點選最後 閱讀原文 檢視原始碼。

更多

640?wx_fmt=png

如果你有好的技術文章想和大家分享,歡迎向我的公眾號投稿,投稿具體細節請在公眾號主頁點選“投稿”選單檢視。

歡迎長按下圖 -> 識別圖中二維碼或者掃一掃關注我的公眾號:

640?wx_fmt=jpeg

覺得有幫助就贊一下唄

讚賞

人讚賞