天天看點

RecyclerView學習(四)----城市導航清單的實作(上)

本文已授權微信公衆号:鴻洋(hongyangAndroid)在微信公衆号平台原創首發。

最近一個月實在是太忙了,部落格也快一個月沒更新了。。。剛好最近公司項目需要一個城市導航的清單,自己搗鼓兩天之後實作的效果圖如下:

RecyclerView學習(四)----城市導航清單的實作(上)

左側的清單根據拼音自動排序,支援頭部懸停,點選Item會提示選擇的城市;右側是一個快速導航欄,點選字母會提示選擇的字母,左側清單會滑動到對應位置,支援導航欄快速滑動。

OK,整體效果就是這樣,真機測試也挺流暢,一起看看怎麼實作這個炫酷的城市導航清單。

1.資料準備

1.建構城市實體類

假如伺服器傳回的是一堆雜亂無章的城市資料,我們需要對這些資料根據拼音的先後順序進行排序。對應的實體類如下:

cityPinyin代表城市名稱的拼音,cityName代表城市名稱,firstPinYin則代表城市拼音的第一個字母,也就是索引。

2.将漢字轉換為拼音

這裡我用的是TinyPinyin,一個适用于Java和Android的快速、低記憶體占用的漢字轉拼音庫。TinyPinyin的特點有:生成的拼音不包含聲調,也不處理多音字,預設一個漢字對應一個拼音;拼音均為大寫;無需初始化,執行效率很高(Pinyin4J的4倍);很低的記憶體占用(小于30KB)。使用起來也很簡單:

比如傳入一個漢字“安慶”,傳回的結果就是“ANQING”

3.根據拼音進行排序

這裡用的是java中的compareto方法,傳回參與比較的前後兩個字元串的asc碼的內插補點,舉個栗子:

若a=”b”,b=”a”,輸出1;

若a=”abcdef”,b=”a”輸出5;

若a=”abcdef”,b=”ace”輸出-1;

即參與比較的兩個字元串如果首字元相同,則比較下一個字元,直到有不同的為止,傳回該不同的字元的asc碼內插補點。

使用的時候實作Comparator接口,傳入需要比較的實體類,然後将傳回值作為 Collections.sort(cityList, pinyinComparator)中的第二個參數,Collections.sort方法會根據這個傳入的int值對cityList進行排序。

2.自定義快速導航欄

1.重寫onDraw()方法

右側快速導航欄是一個自定義View,這裡重點說一下onDraw()方法。

這裡的pinyinList是去除重複的,按照A-Z順序排列的字母索引集合。周遊這個集合,依次繪制出這些字母。在 i 等于 position -1(點選觸摸的位置)的時候,将字型顔色設定為紅色,否則字型顔色為白色。這一點在示範動态圖中有所展現,觸摸點選的字型顔色會改變。

2.重寫onTouchEvent()方法

case MotionEvent.ACTION_DOWN:設定背景顔色,設定字型初始高度,計算觸摸位置,調用invalidate()進行重繪;

case MotionEvent.ACTION_MOVE:與ACTION_DOWN一樣的操作,加上一個判斷,讓滑動的距離大于預設的最小滑動距離才設定滑動有效;

case MotionEvent.ACTION_UP:設定背景顔色,設定字型初始高度,将position設定為0,進行重置操作,調用invalidate()進行重繪;

3.觸摸監聽

螢幕中間是一個自定義的圓形TextView,預設設定為View.GONE,觸摸的時候設定為View.VISIBLE,并将TextView的值設定為點選觸摸的字母。是以我們的接口設計如下:

在MotionEvent.ACTION_DOWN與MotionEvent.ACTION_MOVE的時候:

在MotionEvent.ACTION_UP的時候:

然後讓Activity實作該接口,通過傳過來的boolean值控制圓形TextView是否顯示:

點選觸摸的同時,需要讓recyclerView滑動到對應的位置。周遊cityList數組,得到拼音的第一個字母,與傳遞過來的索引字母進行對比,相等則将

i 設定為selectPosition。最後調用recyclerView.scrollToPosition()方法,滑動到對應的位置,達到索引導航的作用。

3.RecyclerView的懸停實作

1.布局檔案

頭部布局:layout_sticky_header_view.xml,也就是示例圖中紅色的部分,裡面包含一個索引字母TextView

主界面的布局:一共兩層,頭部布局覆寫在RecyclerView上面

子item的布局:線性布局豎直排列,上面引入頭部布局,下面為顯示城市名字的布局

2.建構CityAdapter

這裡重點說一下onBindViewHolder這個方法:

每一個RecyclerView的item的布局裡面都包含一個頭部布局,然後判斷目前item和上一個item的頭部布局裡的索引字母是否相同,來決定是否展示item的頭部布局。

第一個item的頭部布局是顯示的,設定為View.VISIBLE,标記tag為FIRST_STICKY_VIEW;

item布局中,索引字母不相同的頭部布局是顯示的,設定為View.VISIBLE,标記tag為HAS_STICKY_VIEW;

item布局中,索引字母相同的頭部布局是隐藏的,設定為View.GONE,标記tag為NONE_STICKY_VIEW;

最後為每一個item設定一個ContentDescription ,用來記錄并擷取頭部布局展示的資訊。

3.RecyclerView的滑動監聽

主界面的布局中,最上層有一個頭部布局tvStickyHeaderView,通過監聽RecyclerView的滾動,根據RecyclerView的滾動距離,決定頭部布局向上或者向下滾動的距離,實作懸停效果:

1.第一次調用RecyclerView的findChildViewUnder()方法,傳回指定位置的childView,這裡也就是item的頭部布局,因為我們的tvStickyHeaderView展示的肯定是最上面item的頭部布局裡的索引字母資訊。

2.第二次調用RecyclerView的findChildViewUnder()方法,這裡傳回的是固定在螢幕上方那個tvStickyHeaderView下面一個像素位置的RecyclerView的item,根據這個item來更新tvStickyHeaderView要translate多少距離。

3.如果tag為HAS_STICKY_VIEW,表示目前item需要展示頭部布局,那麼根據這個item的getTop和tvStickyHeaderView的高度相差的距離來滾動tvStickyHeaderView;如果tag為NONE_STICKY_VIEW,表示目前item不需要展示頭部布局,那麼就不會引起tvStickyHeaderView的滾動。

<a href="http://www.jianshu.com/p/c596f2e6f587">參考資料</a>

最後使用接口回調處理RecyclerView的點選事件即可。由于篇幅有限,這裡就隻介紹了一些重點。項目完整源碼已經上傳到我的github上:

源碼位址:

<a href="https://github.com/18722527635/MyRecyclerView">https://github.com/18722527635/MyRecyclerView</a>

歡迎Star,fork,提issues,一起進步!

繼續閱讀