RecyclerView的优化:RecycledViewPool
原文出處:https://yrom.net/blog/2015/05/30/recyclerView-tips-1-recycledviewpool/
想必Tabs+ViewPager+ListView 結(jié)合使用的場景在你的Android手機中的各大應(yīng)用里并不少見,比如最為典型的網(wǎng)易新聞。
眾所周知,用RecyclerView可以非常簡單的替代掉ListView。可僅僅就為了將ListView換成RecyclerView,這換湯不換藥的做法顯然不足以讓人心動。
如果我說,再用上RecycledViewPool,可以使你的布局渲染速度、ViewPager滑動流暢度上升一個檔次,你會心動并行動嗎?
RecycledViewPool是什么?
關(guān)于RecycledViewPool,官方文檔是這樣說的:
Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views. This can be useful if you have multiple RecyclerViews with adapters that use the same view types, for example if you have several data sets with the same kinds of item views displayed by a ViewPager. RecyclerView automatically creates a pool for itself if you don’t provide one.
簡言之就是,你可以給RecyclerView設(shè)置一個ViewHolder的對象池,這個池稱為RecycledViewPool,這個對象池可以節(jié)省你創(chuàng)建ViewHolder的開銷,更能避免GC。即便你不給它設(shè)置,它也會自己創(chuàng)建一個。
如此說來,如果多個RecylerView間共用一個RecycledViewPool是不是能讓你的UI更加的“順滑”?
使用RecycledViewPool
RecycledViewPool使用起來也是非常的簡單:先從某個RecyclerView對象中獲得它創(chuàng)建的RecycledViewPool對象,或者是自己實現(xiàn)一個RecycledViewPool對象,然后設(shè)置個接下來創(chuàng)建的每一個RecyclerView即可。
需要注意的是,如果你使用的LayoutManager是LinearLayoutManager或其子類(如GridLayoutManager),需要手動開啟這個特性:
layout.setRecycleChildrenOnDetach(true) RecyclerView view1 = new RecyclerView(context); LinearLayoutManager layout = new LinearLayoutManager(context); layout.setRecycleChildrenOnDetach(true); view1.setLayoutManager(layout); RecycledViewPool pool = view1.getRecycledViewPool(); //... RecyclerView view2 = new RecyclerView(context); //... (set layout manager) view2.setRecycledViewPool(pool); //... RecyclerView view3 = new RecyclerView(context); //...(set layout manager) view3.setRecycledViewPool(pool);ViewPager中使用原理同上,只是你得通過ViewPagerAdapter傳遞個下一個RecyclerView。
代碼示例如下:
public class PagerActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_pager);ViewPager pager = (ViewPager) findViewById(R.id.pager);pager.setAdapter(new PageAdapter(getSupportFragmentManager()));}static class PageAdapter extends FragmentPagerAdapter{RecyclerView.RecycledViewPool mPool = new RecyclerView.RecycledViewPool();public PageAdapter(FragmentManager fm) {super(fm);}@Overridepublic Fragment getItem(int i) {RecyclerViewFragment f = new RecyclerViewFragment();f.mPool = mPool;return f;}// ...}public static class RecyclerViewFragment extends Fragment{RecyclerView.RecycledViewPool mPool;@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {RecyclerView view = new RecyclerView(inflater.getContext());LinearLayoutManager layout = new LinearLayoutManager(inflater.getContext());layout.setRecycleChildrenOnDetach(true);view.setLayoutManager(layout);if (mPool != null) {view.setRecycledViewPool(mPool);}view.setAdapter(...);return view;}// ...} }布局xml:activity_pager.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><android.support.v4.view.ViewPager android:id="@+id/pager"android:layout_width="match_parent"android:layout_height="match_parent"><android.support.v4.view.PagerTitleStrip android:id="@+id/tabs"android:layout_width="match_parent"android:layout_height="wrap_content"/></android.support.v4.view.ViewPager> </LinearLayout>詳見 https://gist.github.com/yrom/728237025575005a8fd3
More Tips
RecycledViewPool是依據(jù)ItemViewType來索引ViewHolder的,所以你必須確保共享的RecyclerView的Adapter是同一個,或view type 是不會沖突的。
RecycledViewPool可以自主控制需要緩存的ViewHolder數(shù)量:
mPool.setMaxRecycledViews(itemViewType, number); 畢竟池子里的水并不是越深越好。
RecyclerView可以設(shè)置自己所需要的ViewHolder數(shù)量,只有超過這個數(shù)量的detached ViewHolder才會丟進ViewPool中與別的RecyclerView共享。
recyclerView.setItemViewCacheSize(10);在合適的時機,RecycledViewPool會自我清除掉所持有的ViewHolder對象引用,不用擔(dān)心池子會“側(cè)漏”。當(dāng)然你也可以在你認(rèn)為合適的時機手動調(diào)用clear()
用RecyclerView快速實現(xiàn)列表頁面
如一個簡單的列表場景:TodoList。
分頁加載現(xiàn)有Todo
現(xiàn)有數(shù)據(jù)基礎(chǔ)上增、刪、改
RecyclerView的使用在此就不贅述了,本文主要討論RecyclerView.Adapter的實現(xiàn)
使用最簡單的ArrayList實現(xiàn),如下:
class ListAdapter extends RecyclerView.Adapter<TodoViewHolder> {final ArrayList<Item> mData;final LayoutInflater mLayoutInflater;public SortedListAdapter(Context context) {mLayoutInflater = LayoutInflater.from(context);mData = new ArrayList<>();}public void addItem(Item item) {mData.add(item); // 需要自己通知更新}@Overridepublic TodoViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {return new TodoViewHolder (mLayoutInflater.inflate(R.layout.list_todo_item, parent, false));}@Overridepublic void onBindViewHolder(TodoViewHolder holder, int position) {holder.bindTo(mData.get(position));}@Overridepublic int getItemCount() {return mData.size();} }這樣的Adapter一個顯而易見的問題就是,如何做數(shù)據(jù)的去重。
添加一項數(shù)據(jù):最簡單的是在addItem()之前,遍歷一次mData,定位后再決定是插入還是更新現(xiàn)有數(shù)據(jù),并調(diào)用notifyItemInserted(pos)。
添加多個數(shù)據(jù):多次重復(fù)上面的方法…
對于少量數(shù)據(jù)來說這樣做并不見得有什么問題,而且寫得多了,都有自己封裝好的諸如ArrayObjectAdapter之類方便使用。
這樣就夠了嗎?
答案肯定是不。Android Support Library 悄悄給我們提供了一個叫SortedList的工具類,它默默的藏在support庫的角落中,鮮為人知。
SortedList?
文檔對它的定義:
構(gòu)造一個SortedList需要實現(xiàn)它的回調(diào)SortedList.Callback,并由其來定義數(shù)據(jù)的排序和數(shù)據(jù)的唯一性。它有一個實現(xiàn)類SortedListAdapterCallback就是RecyclerView.Adapter與SortedList交互的秘密武器。
示例
改造后的ListAdapter:
class SortedListAdapter extends RecyclerView.Adapter<TodoViewHolder> {final SortedList<Item> mData;final LayoutInflater mLayoutInflater;public SortedListAdapter(Context context) {mLayoutInflater = LayoutInflater.from(context);mData = new SortedList<Item>(Item.class, new SortedListAdapterCallback<Item>(this){@Overridepublic int compare(Item t0, Item t1) {// 實現(xiàn)這個方法來定義Item的顯示順序int txtComp = t0.mText.compareTo(t1.mText);if (txtComp != 0) {return txtComp;}if (t0.id < t1.id) {return -1;} else if (t0.id > t1.id) {return 1;}return 0;}@Overridepublic boolean areContentsTheSame(Item oldItem,Item newItem) {// 比較兩個Item的內(nèi)容是否一致,如不一致則會調(diào)用adapter的notifyItemChanged()return oldItem.mText.equals(newItem.mText);}@Overridepublic boolean areItemsTheSame(Item item1, Item item2) {// 兩個Item是不是同一個東西,// 它們的內(nèi)容或許不一樣,但id相同代表就是同一個return item1.id == item2.id;}});}public void addItem(Item item) {mData.add(item);// 會通過SortedListAdapterCallback自動通知更新}...@Overridepublic int getItemCount() {return mData.size();} }雖然相對ListAdapter代碼量變多了,但是調(diào)用者卻再也不用關(guān)心數(shù)據(jù)的去重與通知更新的問題。這一切都有SortedListAdapterCallback幫你自動處理好了。
單單只這一個好處其實并不值得勞師動眾去改掉現(xiàn)有Adapter使用SortedList,但它另外還有一個令人稱贊并喜愛的功能:批量更新(Batched Updates)。
就如實現(xiàn)添加多個數(shù)據(jù):
void addItems(List<Item> items){mData.beginBatchedUpdates(); // 開始批量更新mData.addAll(items); // 更新一批數(shù)據(jù)mData.endBatchedUpdates(); // 結(jié)束更新 }批量刪除:
void deleteItems(List<Item> items){mData.beginBatchedUpdates(); // 開始批量更新for(Item item : items){ // 刪除一批數(shù)據(jù)mData.remove(item);}mData.endBatchedUpdates(); // 結(jié)束更新 }等等。
如例子所示,調(diào)用beginBatchedUpdates()之后,所有的對SortedList操作都會等到endBatchedUpdates()之后一起生效。
完整的示例見:官方Support庫Samples ($ANDROID_SDK/extras/android/support/samples/Support7Demos)
More Tips
列表無序的情況,可以用其id或原始數(shù)據(jù)List的index來比較排序,只要確保能正確實現(xiàn)compare即可
如無需批量更新,或無頻繁的增刪改,其實用前面的ListAdapter比較好。
總之:如果你的列表需要批量更新或者頻繁刪改,且剛好有明確的先后順序,快使用SortedList。
總結(jié)
以上是生活随笔為你收集整理的RecyclerView的优化:RecycledViewPool的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android WebView 和 ja
- 下一篇: Android开发中遇到的bug