Android RecyclerView 必知必會

d0f4sd01dd41 7年前發布 | 11K 次閱讀 Android開發 移動開發 RecyclerView

導語

RecyclerView是Android 5.0提出的新UI控件,可以用來代替傳統的ListView。

Bugly之前也發過一篇相關文章,講解了  RecyclerView 與 ListView 在緩存機制上的一些區別:

今天精神哥來給大家詳細介紹關于  RecyclerView,你需要了解 的方方面面。

前言

下文中Demo的源代碼地址:

https://github.com/xiazdong/RecyclerViewDemo

(點擊文末閱讀原文,直接訪問該項目)

  • Demo1: RecyclerView添加HeaderView和FooterView,ItemDecoration范例。

  • Demo2: ListView實現局部刷新。

  • Demo3: RecyclerView實現拖拽、側滑刪除。

  • Demo4: RecyclerView閃屏問題。

  • Demo5: RecyclerView實現 setEmptyView() 。

  • Demo6: RecyclerView實現萬能適配器,瀑布流布局,嵌套滑動機制。

基本概念

RecyclerView是Android 5.0提出的新UI控件,位于support-v7包中,可以通過在build.gradle中添加 compile 'com.android.support:recyclerview-v7:24.2.1' 導入。

RecyclerView的官方定義如下:

A flexible view for providing a limited window into a large data set.

從定義可以看出,flexible(可擴展性)是RecyclerView的特點。不過我們發現和ListView有點像,本文后面會介紹RecyclerView和ListView的區別。

為什么會出現RecyclerView?

RecyclerView并不會完全替代ListView(這點從ListView沒有被標記為@Deprecated可以看出),兩者的使用場景不一樣。但是RecyclerView的出現會讓很多開源項目被廢棄,例如橫向滾動的ListView, 橫向滾動的GridView, 瀑布流控件,因為RecyclerView能夠實現所有這些功能。

比如有一個需求是屏幕豎著的時候的顯示形式是ListView,屏幕橫著的時候的顯示形式是2列的GridView,此時如果用RecyclerView,則通過設置LayoutManager一行代碼實現替換。

ListView vs RecyclerView

ListView相比RecyclerView,有一些優點:

  • addHeaderView() , addFooterView() 添加頭視圖和尾視圖。

  • 通過”android:divider”設置自定義分割線。

  • setOnItemClickListener() 和 setOnItemLongClickListener() 設置點擊事件和長按事件。

這些功能在RecyclerView中都沒有直接的接口,要自己實現(雖然實現起來很簡單),因此如果只是實現簡單的顯示功能,ListView無疑更簡單。

RecyclerView相比ListView,有一些明顯的優點:

  • 默認已經實現了View的復用,不需要類似 if(convertView == null) 的實現,而且回收機制更加完善。

  • 默認支持局部刷新。

  • 容易實現添加item、刪除item的動畫效果。

  • 容易實現拖拽、側滑刪除等功能。

RecyclerView是一個插件式的實現,對各個功能進行解耦,從而擴展性比較好。

ListView實現局部刷新

我們都知道ListView通過 adapter.notifyDataSetChanged() 實現ListView的更新,這種更新方法的缺點是全局更新,即對每個Item View都進行重繪。但事實上很多時候,我們只是更新了其中一個Item的數據,其他Item其實可以不需要重繪。

這里給出ListView實現局部更新的方法:

可以看出,我們通過ListView的 getChildAt() 來獲得需要更新的View,然后通過 getTag() 獲得ViewHolder,從而實現更新。

標準用法

RecyclerView的標準實現步驟如下:

  • 創建Adapter:創建一個繼承 RecyclerView.Adapter<VH> 的Adapter類(VH是ViewHolder的類名),記為NormalAdapter。

  • 創建ViewHolder:在NormalAdapter中創建一個繼承 RecyclerView.ViewHolder 的靜態內部類,記為VH。ViewHolder的實現和ListView的ViewHolder實現幾乎一樣。

  • 在NormalAdapter中實現:

    • VH onCreateViewHolder(ViewGroup parent, int viewType) : 映射Item Layout Id,創建VH并返回。

    • void onBindViewHolder(VH holder, int position) : 為holder設置指定數據。

    • int getItemCount() : 返回Item的個數。

    </li> </ul>

    可以看出,RecyclerView將ListView中 getView() 的功能拆分成了 onCreateViewHolder() 和 onBindViewHolder() 。

    基本的Adapter實現如下:

    創建完Adapter,接著對RecyclerView進行設置,一般來說,需要為RecyclerView進行四大設置,也就是后文說的四大組成:Adapter(必選),Layout Manager(必選),Item Decoration(可選,默認為空), Item Animator(可選,默認為DefaultItemAnimator)。

    需要注意的是在 onCreateViewHolder() 中,映射Layout必須為

    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_1, parent, false);

    而不能是:

    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_1, null);

    如果要實現ListView的效果,只需要設置Adapter和Layout Manager,如下:

    List<String> data = initData();
    RecyclerView rv = (RecyclerView) findViewById(R.id.rv);
    rv.setLayoutManager(new LinearLayoutManager(this));
    rv.setAdapter(new NormalAdapter(data));

    ListView只提供了 notifyDataSetChanged() 更新整個視圖,這是很不合理的。RecyclerView提供了 notifyItemInserted() , notifyItemRemoved() , notifyItemChanged() 等API更新單個或某個范圍的Item視圖。

    四大組成

    RecyclerView的四大組成是:

    • Adapter:為Item提供數據。

    • Layout Manager:Item的布局。

    • Item Animator:添加、刪除Item動畫。

    • Item Decoration:Item之間的Divider。

    Adapter

    Adapter的使用方式前面已經介紹了,功能就是為RecyclerView提供數據,這里主要介紹萬能適配器的實現。其實萬能適配器的概念在ListView就已經存在了,即 base-adapter-helper 。

    這里我們只針對RecyclerView,聊聊萬能適配器出現的原因。為了創建一個RecyclerView的Adapter,每次我們都需要去做重復勞動,包括重寫 onCreateViewHolder() , getItemCount() 、創建ViewHolder,并且實現過程大同小異,因此萬能適配器出現了,他能通過以下方式快捷地創建一個Adapter:

    是不是很方便。當然復雜情況也可以輕松解決。

    這里講解下萬能適配器的實現思路。

    我們通過 public abstract class QuickAdapter<T> extends RecyclerView.Adapter<QuickAdapter.VH> 定義萬能適配器QuickAdapter類,T是列表數據中每個元素的類型,QuickAdapter.VH是QuickAdapter的ViewHolder實現類,稱為萬能ViewHolder。

    首先介紹QuickAdapter.VH的實現:

    其中的關鍵點在于通過 SparseArray<View> 存儲item view的控件, getView(int id) 的功能就是通過id獲得對應的View(首先在mViews中查詢是否存在,如果沒有,那么 findViewById() 并放入mViews中,避免下次再執行 findViewById() )。

    QuickAdapter的實現如下:

    其中:

    • getLayoutId(int viewType) 是根據viewType返回布局ID。

    • convert() 做具體的bind操作。

    就這樣,萬能適配器實現完成了。

    Item Decoration

    RecyclerView通過 addItemDecoration() 方法添加item之間的分割線。Android并沒有提供實現好的Divider,因此任何分割線樣式都需要自己實現。

    方法是:創建一個類并繼承RecyclerView.ItemDecoration,重寫以下兩個方法:

    • onDraw(): 繪制分割線。

    • getItemOffsets(): 設置分割線的寬、高。

    Google在sample中給了一個參考的實現類:DividerItemDecoration,這里我們通過分析這個例子來看如何自定義Item Decoration。

    首先看構造函數,構造函數中獲得系統屬性 android:listDivider ,該屬性是一個Drawable對象。

    因此如果要設置,則需要在value/styles.xml中設置:

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="android:listDivider">@drawable/item_divider</item></style>

    接著來看 getItemOffsets() 的實現:

    這里只看 mOrientation == VERTICAL_LIST 的情況,outRect是當前item四周的間距,類似margin屬性,現在設置了該item下間距為 mDivider.getIntrinsicHeight() 。

    那么 getItemOffsets() 是怎么被調用的呢?

    RecyclerView繼承了ViewGroup,并重寫了 measureChild() ,該方法在 onMeasure() 中被調用,用來計算每個child的大小,計算每個child大小的時候就需要加上 getItemOffsets() 設置的外間距:

    這里我們只考慮 mOrientation == VERTICAL_LIST 的情況,DividerItemDecoration的 onDraw() 實際上調用了 drawVertical() :

    那么 onDraw() 是怎么被調用的呢?還有ItemDecoration還有一個方法 onDrawOver() ,該方法也可以被重寫,那么 onDraw() 和 onDrawOver() 之間有什么關系呢?

    我們來看下面的代碼:

    根據View的繪制流程,首先調用RecyclerView重寫的 draw() 方法,隨后 super.draw() 即調用View的 draw() ,該方法會先調用 onDraw() (這個方法在RecyclerView重寫了),再調用 dispatchDraw() 繪制children。因此:ItemDecoration的 onDraw() 在繪制Item之前調用,ItemDecoration的 onDrawOver() 在繪制Item之后調用。

    當然,如果只需要實現Item之間相隔一定距離,那么只需要為Item的布局設置margin即可,沒必要自己實現ItemDecoration這么麻煩。

    Layout Manager

    LayoutManager負責RecyclerView的布局,其中包含了Item View的獲取與回收。這里我們簡單分析LinearLayoutManager的實現。

    對于LinearLayoutManager來說,比較重要的幾個方法有:

    • onLayoutChildren() : 對RecyclerView進行布局的入口方法。

    • fill() : 負責填充RecyclerView。

    • scrollVerticallyBy() :根據手指的移動滑動一定距離,并調用 fill() 填充。

    • canScrollVertically() 或 canScrollHorizontally() : 判斷是否支持縱向滑動或橫向滑動。

    onLayoutChildren() 的核心實現如下:

    RecyclerView的回收機制有個重要的概念,即將回收站分為Scrap Heap和Recycle Pool,其中Scrap Heap的元素可以被直接復用,而不需要調用 onBindViewHolder() 。 detachAndScrapAttachedViews() 會根據情況,將原來的Item View放入Scrap Heap或Recycle Pool,從而在復用時提升效率。

    fill() 是對剩余空間不斷地調用 layoutChunk() ,直到填充完為止。 layoutChunk() 的核心實現如下:

    其中 next() 調用了 getViewForPosition(currentPosition) ,該方法是從RecyclerView的回收機制實現類Recycler中獲取合適的View,在后文的回收機制中會介紹該方法的具體實現。

    如果要自定義LayoutManager,可以參考:

    創建一個 RecyclerView LayoutManager – Part 1

    https://github.com/hehonghui/android-tech-frontier/blob/master/issue-9/%E5%88%9B%E5%BB%BA-RecyclerView-LayoutManager-Part-1.md

    創建一個 RecyclerView LayoutManager – Part 2

    https://github.com/hehonghui/android-tech-frontier/blob/master/issue-13/%E5%88%9B%E5%BB%BA-RecyclerView-LayoutManager-Part-2.md

    創建一個 RecyclerView LayoutManager – Part 3

    https://github.com/hehonghui/android-tech-frontier/blob/master/issue-13/%E5%88%9B%E5%BB%BA-RecyclerView-LayoutManager-Part-3.md

    Item Animator

    RecyclerView能夠通過 mRecyclerView.setItemAnimator(ItemAnimator animator) 設置添加、刪除、移動、改變的動畫效果。RecyclerView提供了默認的ItemAnimator實現類:DefaultItemAnimator。這里我們通過分析DefaultItemAnimator的源碼來介紹如何自定義Item Animator。

    DefaultItemAnimator繼承自SimpleItemAnimator,SimpleItemAnimator繼承自ItemAnimator。

    首先我們介紹ItemAnimator類的幾個重要方法:

    • animateAppearance() : 當ViewHolder出現在屏幕上時被調用(可能是add或move)。

    • animateDisappearance() : 當ViewHolder消失在屏幕上時被調用(可能是remove或move)。

    • animatePersistence() : 在沒調用 notifyItemChanged() 和 notifyDataSetChanged() 的情況下布局發生改變時被調用。

    • animateChange() : 在顯式調用 notifyItemChanged() 或 notifyDataSetChanged() 時被調用。

    • runPendingAnimations(): RecyclerView動畫的執行方式并不是立即執行,而是每幀執行一次,比如兩幀之間添加了多個Item,則會將這些將要執行的動畫Pending住,保存在成員變量中,等到下一幀一起執行。該方法執行的前提是前面 animateXxx() 返回true。

    • isRunning(): 是否有動畫要執行或正在執行。

    • dispatchAnimationsFinished(): 當全部動畫執行完畢時被調用。

    上面用斜體字標識的方法比較難懂,不過沒關系,因為Android提供了SimpleItemAnimator類(繼承自ItemAnimator),該類提供了一系列更易懂的API,在自定義Item Animator時只需要繼承SimpleItemAnimator即可:

    • animateAdd(ViewHolder holder): 當Item添加時被調用。

    • animateMove(ViewHolder holder, int fromX, int fromY, int toX, int toY): 當Item移動時被調用。

    • animateRemove(ViewHolder holder): 當Item刪除時被調用。

    • animateChange(ViewHolder oldHolder, ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop): 當顯式調用 notifyItemChanged() 或 notifyDataSetChanged() 時被調用。

    對于以上四個方法,注意兩點:

    • 當Xxx動畫開始執行前(在 runPendingAnimations() 中)需要調用 dispatchXxxStarting(holder) ,執行完后需要調用 dispatchXxxFinished(holder) 。

    • 這些方法的內部實際上并不是書寫執行動畫的代碼,而是將需要執行動畫的Item全部存入成員變量中,并且返回值為true,然后在 runPendingAnimations() 中一并執行。

    DefaultItemAnimator類是RecyclerView提供的默認動畫類。我們通過閱讀該類源碼學習如何自定義Item Animator。我們先看DefaultItemAnimator的成員變量:

    DefaultItemAnimator實現了SimpleItemAnimator的 animateAdd() 方法,該方法只是將該item添加到mPendingAdditions中,等到 runPendingAnimations() 中執行。

    接著看 runPendingAnimations() 的實現,該方法是執行remove,move,change,add動畫,執行順序為:remove動畫最先執行,隨后move和change并行執行,最后是add動畫。為了簡化,我們將remove,move,change動畫執行過程省略,只看執行add動畫的過程,如下:

    為了防止在執行add動畫時外面有新的add動畫添加到mPendingAdditions中,從而導致執行add動畫錯亂,這里將mPendingAdditions的內容移動到局部變量additions中,然后遍歷additions執行動畫。

    在 runPendingAnimations() 中, animateAddImpl() 是執行add動畫的具體方法,其實就是將itemView的透明度從0變到1(在 animateAdd() 中已經將view的透明度變為0),實現如下:

    從DefaultItemAnimator類的實現來看,發現自定義Item Animator好麻煩,需要繼承SimpleItemAnimator類,然后實現一堆方法。別急,recyclerview-animators解救你,原因如下:

    首先,recyclerview-animators提供了一系列的Animator,比如FadeInAnimator,ScaleInAnimator。其次,如果該庫中沒有你滿意的動畫,該庫提供了BaseItemAnimator類,該類繼承自SimpleItemAnimator,進一步封裝了自定義Item Animator的代碼,使得自定義Item Animator更方便,你只需要關注動畫本身。如果要實現DefaultItemAnimator的代碼,只需要以下實現:

    是不是比繼承SimpleItemAnimator方便多了。

    對于RecyclerView的Item Animator,有一個常見的坑就是”閃屏問題”。這個問題的描述是:當Item視圖中有圖片和文字,當更新文字并調用 notifyItemChanged() 時,文字改變的同時圖片會閃一下。這個問題的原因是當調用 notifyItemChanged() 時,會調用DefaultItemAnimator的 animateChangeImpl() 執行change動畫,該動畫會使得Item的透明度從0變為1,從而造成閃屏。

    解決辦法很簡單,在 rv.setAdapter() 之前調用 ((SimpleItemAnimator)rv.getItemAnimator()).setSupportsChangeAnimations(false) 禁用change動畫。

    拓展RecyclerView

    添加setOnItemClickListener接口

    RecyclerView默認沒有像ListView一樣提供 setOnItemClickListener() 接口,而 RecyclerView無法添加onItemClickListener最佳的高效解決方案 (http://blog.csdn.net/liaoinstan/article/details/51200600)這篇文章給出了通過 recyclerView.addOnItemTouchListener(...) 添加點擊事件的方法,但我認為根本沒有必要費這么大勁對外暴露這個接口,因為我們完全可以把點擊事件的實現寫在Adapter的 onBindViewHolder() 中,不暴露出來。具體方法就是通過:

       public void onBindViewHolder(VH holder, int position) {
            holder.itemView.setOnClickListener(...);
        }

    添加HeaderView和FooterView

    RecyclerView默認沒有提供類似 addHeaderView() 和 addFooterView() 的API,因此這里介紹如何優雅地實現這兩個接口。

    如果你已經實現了一個Adapter,現在想為這個Adapter添加 addHeaderView() 和 addFooterView() 接口,則需要在Adapter中添加幾個Item Type,然后修改 getItemViewType() , onCreateViewHolder() , onBindViewHolder() , getItemCount() 等方法,并添加switch語句進行判斷。那么如何在不破壞原有Adapter實現的情況下完成呢?

    這里引入裝飾器(Decorator)設計模式,該設計模式通過組合的方式,在不破話原有類代碼的情況下,對原有類的功能進行擴展。

    這恰恰滿足了我們的需求。我們只需要通過以下方式為原有的Adapter(這里命名為NormalAdapter)添加 addHeaderView() 和 addFooterView() 接口:

    是不是看起來特別優雅。具體實現思路其實很簡單,創建一個繼承 RecyclerView.Adapter<RecyclerView.ViewHolder> 的類,并重寫常見的方法,然后通過引入ITEM TYPE的方式實現:

    添加setEmptyView

    ListView提供了 setEmptyView() 設置Adapter數據為空時的View視圖。RecyclerView雖然沒提供直接的API,但是也可以很簡單地實現。

    • 創建一個繼承RecyclerView的類,記為EmptyRecyclerView。

    • 通過 getRootView().addView(emptyView) 將空數據時顯示的View添加到當前View的層次結構中。

    • 通過AdapterDataObserver監聽RecyclerView的數據變化,如果adapter為空,那么隱藏RecyclerView,顯示EmptyView。

    具體實現如下:

    拖拽、側滑刪除

    Android提供了ItemTouchHelper類,使得RecyclerView能夠輕易地實現滑動和拖拽,此處我們要實現上下拖拽和側滑刪除。首先創建一個繼承自 ItemTouchHelper.Callback 的類,并重寫以下方法:

    • getMovementFlags() : 設置支持的拖拽和滑動的方向,此處我們支持的拖拽方向為上下,滑動方向為從左到右和從右到左,內部通過 makeMovementFlags() 設置。

    • onMove() : 拖拽時回調。

    • onSwiped() : 滑動時回調。

    • onSelectedChanged() : 狀態變化時回調,一共有三個狀態,分別是ACTION_STATE_IDLE(空閑狀態),ACTION_STATE_SWIPE(滑動狀態),ACTION_STATE_DRAG(拖拽狀態)。此方法中可以做一些狀態變化時的處理,比如拖拽的時候修改背景色。

    • clearView() : 用戶交互結束時回調。此方法可以做一些狀態的清空,比如拖拽結束后還原背景色。

    • isLongPressDragEnabled() : 是否支持長按拖拽,默認為true。如果不想支持長按拖拽,則重寫并返回false。

    具體實現如下:

    然后通過以下代碼為RecyclerView設置該滑動、拖拽功能:

    ItemTouchHelper helper = new ItemTouchHelper(new SimpleItemTouchCallback(adapter, data));
    helper.attachToRecyclerView(recyclerview);

    前面拖拽的觸發方式只有長按,如果想支持觸摸Item中的某個View實現拖拽,則核心方法為 helper.startDrag(holder) 。首先定義接口:

    interface OnStartDragListener{ void startDrag(RecyclerView.ViewHolder holder); }

    然后讓Activity實現該接口:

    public MainActivity extends Activity implements OnStartDragListener{
        ...
        public void startDrag(RecyclerView.ViewHolder holder) {
            mHelper.startDrag(holder);
        }
    }

    如果要對ViewHolder的text對象支持觸摸拖拽,則在Adapter中的 onBindViewHolder() 中添加:

    其中mListener是在創建Adapter時將實現OnStartDragListener接口的Activity對象作為參數傳進來。

    回收機制

    ListView回收機制

    ListView為了保證Item View的復用,實現了一套回收機制,該回收機制的實現類是RecycleBin,他實現了兩級緩存:

    • View[] mActiveViews : 緩存屏幕上的View,在該緩存里的View不需要調用 getView() 。

    • ArrayList<View>[] mScrapViews; : 每個Item Type對應一個列表作為回收站,緩存由于滾動而消失的View,此處的View如果被復用,會以參數的形式傳給 getView() 。

    接下來我們通過源碼分析ListView是如何與RecycleBin交互的。其實ListView和RecyclerView的layout過程大同小異,ListView的布局函數是 layoutChildren() ,實現如下:

    其中 fillXxx() 實現了對Item View進行填充,該方法內部調用了 makeAndAddView() ,實現如下:

    其中, getActiveView() 是從mActiveViews中獲取合適的View,如果獲取到了,則直接返回,而不調用 obtainView() ,這也印證了如果從mActiveViews獲取到了可復用的View,則不需要調用 getView() 。

    obtainView() 是從mScrapViews中獲取合適的View,然后以參數形式傳給了 getView() ,實現如下:

    接下去我們介紹 getScrapView(position) 的實現,該方法通過position得到Item Type,然后根據Item Type從mScrapViews獲取可復用的View,如果獲取不到,則返回null,具體實現如下:

    RecyclerView回收機制

    RecyclerView和ListView的回收機制非常相似,但是ListView是以View作為單位進行回收,RecyclerView是以ViewHolder作為單位進行回收。Recycler是RecyclerView回收機制的實現類,他實現了四級緩存:

    • mAttachedScrap: 緩存在屏幕上的ViewHolder。

    • mCachedViews: 緩存屏幕外的ViewHolder,默認為2個。ListView對于屏幕外的緩存都會調用 getView() 。

    • mViewCacheExtensions: 需要用戶定制,默認不實現。

    • mRecyclerPool: 緩存池,多個RecyclerView共用。

    在上文Layout Manager中已經介紹了RecyclerView的layout過程,但是一筆帶過了 getViewForPosition() ,因此此處介紹該方法的實現。

    從上述實現可以看出,依次從mAttachedScrap, mCachedViews, mViewCacheExtension, mRecyclerPool尋找可復用的ViewHolder,如果是從mAttachedScrap或mCachedViews中獲取的ViewHolder,則不會調用 onBindViewHolder() ,mAttachedScrap和mCachedViews也就是我們所說的Scrap Heap;而如果從mViewCacheExtension或mRecyclerPool中獲取的ViewHolder,則會調用 onBindViewHolder() 。

    RecyclerView局部刷新的實現原理也是基于RecyclerView的回收機制,即能直接復用的ViewHolder就不調用 onBindViewHolder() 。

    嵌套滑動機制

    Android 5.0推出了嵌套滑動機制,在之前,一旦子View處理了觸摸事件,父View就沒有機會再處理這次的觸摸事件,而嵌套滑動機制解決了這個問題,能夠實現如下效果:

    為了支持嵌套滑動,子View必須實現NestedScrollingChild接口,父View必須實現NestedScrollingParent接口,而RecyclerView實現了NestedScrollingChild接口,而CoordinatorLayout實現了NestedScrollingParent接口,上圖是實現CoordinatorLayout嵌套RecyclerView的效果。

    為了實現上圖的效果,需要用到的組件有:

    • CoordinatorLayout: 布局根元素。

    • AppBarLayout: 包裹的內容作為應用的Bar。

    • CollapsingToolbarLayout: 實現可折疊的ToolBar。

    • ToolBar: 代替ActionBar。

    實現中需要注意的點有:

    • 我們為ToolBar的 app:layout_collapseMode 設置為pin,表示折疊之后固定在頂端,而為ImageView的 app:layout_collapseMode 設置為parallax,表示視差模式,即漸變的效果。

    • 為了讓RecyclerView支持嵌套滑動,還需要為它設置 app:layout_behavior="@string/appbar_scrolling_view_behavior" 。

    • 為CollapsingToolbarLayout設置 app:layout_scrollFlags="scroll|exitUntilCollapsed" ,其中scroll表示滾動出屏幕,exitUntilCollapsed表示退出后折疊。

    具體實現參見Demo6。

    回顧

    回顧整篇文章,發現我們已經實現了RecyclerView的很多擴展功能,包括:打造萬能適配器、添加Item事件、添加頭視圖和尾視圖、設置空布局、側滑拖拽。BaseRecyclerViewAdapterHelper是一個比較火的RecyclerView擴展庫,仔細一看發現,這里面80%的功能在我們這篇文章中都實現了。

     

     

    來自:http://mp.weixin.qq.com/s/CzrKotyupXbYY6EY2HP_dA

     

 本文由用戶 d0f4sd01dd41 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!