[Android]使用RecyclerView替代ListView(二)

如下內容爲原創,轉載請註明:html

來自每天博客:http://www.cnblogs.com/tiantianbyconan/p/4242541.html
java

 

之前寫過一篇「[Android]使用AdapterTypeRender對不一樣類型的item數據到UI的渲染http://www.cnblogs.com/tiantianbyconan/p/3992843.html)」,用於在有不少不一樣類型不一樣佈局的item的時候怎麼去較好的進行view的綁定和數據的渲染,可是這個是針對ListView寫的。此次我針對RecyclerView也從新實現了一遍。android

接下來演示下怎麼去渲染不一樣類型的item,而且使它支持下拉刷新,滾動到底部顯示加載進度條顯示。git

如下全部的封裝都在AndroidBucket項目中:https://github.com/wangjiegulu/AndroidBucket/tree/master/src/com/wangjie/androidbucket/support/recyclerview
github

 

使用的方式以下:緩存

 1 final View footerView = LayoutInflater.from(context).inflate(R.layout.recycler_view_item_type_footer, null);
 2 //        不知道爲何在xml設置的「android:layout_width="match_parent"」無效了,須要在這裏從新設置
 3         LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
 4         footerView.setLayoutParams(lp);
 5 
 6         recyclerView.setHasFixedSize(true);
 7 
 8         final ABaseLinearLayoutManager layoutManager = new ABaseLinearLayoutManager(context);
 9         layoutManager.setOnRecyclerViewScrollLocationListener(recyclerView, new OnRecyclerViewScrollLocationListener() {
10             @Override
11             public void onTopWhenScrollIdle(RecyclerView recyclerView) {
12                 Logger.d(TAG, "onTopWhenScrollIdle...");
13             }
14 
15             @Override
16             public void onBottomWhenScrollIdle(RecyclerView recyclerView) {
17                 Logger.d(TAG, "onBottomWhenScrollIdle...");
18                 footerView.setVisibility(View.VISIBLE);
19                 ThreadPool.go(new Runtask<Object, Object>() {
20                     @Override
21                     public Object runInBackground() {
22                         ABThreadUtil.sleep(3000);
23                         return null;
24                     }
25 
26                     @Override
27                     public void onResult(Object result) {
28                         super.onResult(result);
29                         refreshLayout.setRefreshing(false);
30                         footerView.setVisibility(View.GONE);
31                     }
32                 });
33             }
34         });
35         layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
36         layoutManager.getRecyclerViewScrollManager().addScrollListener(recyclerView, new OnRecyclerViewScrollListener() {
37             @Override
38             public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
39             }
40 
41             @Override
42             public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
43                 refreshLayout.setEnabled(layoutManager.findFirstCompletelyVisibleItemPosition() == 0);
44             }
45         });
46         recyclerView.setLayoutManager(layoutManager);
47 
48         initData();
49 
50         adapter = new PersonTypeAdapter(context, personList, null, footerView);
51         adapter.setOnRecyclerViewListener(this);
52         recyclerView.setAdapter(adapter);
53 
54         refreshLayout.setColorSchemeColors(Color.BLUE, Color.RED, Color.YELLOW, Color.GREEN);
55         refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
56             @Override
57             public void onRefresh() {
58                 ThreadPool.go(new Runtask<Object, Object>() {
59                     @Override
60                     public Object runInBackground() {
61                         ABThreadUtil.sleep(3000);
62                         return null;
63                     }
64 
65                     @Override
66                     public void onResult(Object result) {
67                         super.onResult(result);
68                         refreshLayout.setRefreshing(false);
69                         footerView.setVisibility(View.GONE);
70                     }
71                 });
72             }
73         });

 

如上述代碼:ide

Line1:從佈局文件中inflate出一個View實例,這個View實例,下面會被用於做爲下載提示的footer。佈局

Line8:生成一個ABaseLinearLayoutManager實例,顯然這個類是繼承LinearLayoutManager以後擴展的,詳見:https://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/support/recyclerview/layoutmanager/ABaseLinearLayoutManager.java,這個類對滑動的監聽進行了擴展,能夠監聽滑動到頂部或者底部的事件ui

Line9~34:設置滑動到頂部或底部的監聽器,而後一旦滑動到底部則加載更多數據。this

Line36~45:也是設置滑動監聽器,滑動過程當中若是不是處在第一個item,若是是,則就能夠下拉使用SwipeRefreshLayout進行刷新,若是不是則,僅用SwipeRefreshLayout。之因此須要作這個處理,是由於Google事件處理的一個bug--。

Line50:使用了一個PersonTypeAdapter,這個類繼承了ABRecyclerViewTypeExtraViewAdapter這個類繼承RecyclerView.Adapter實現了對不一樣type渲染數據的封裝。

 

接下來重點分析下ABRecyclerViewTypeExtraViewAdapter這個類(這個類在日常使用時不須要關注):

  1 /**
  2  * Author: wangjie
  3  * Email: tiantian.china.2@gmail.com
  4  * Date: 1/22/15.
  5  */
  6 public abstract class ABRecyclerViewTypeExtraViewAdapter extends RecyclerView.Adapter<ABRecyclerViewTypeExtraHolder> {
  7     private static final int TYPE_HEADER_VIEW = 0x7683;
  8     private View headerView;
  9     private static final int TYPE_FOOTER_VIEW = 0x7684;
 10     private View footerView;
 11     private int extraCount;
 12 
 13     protected ABRecyclerViewTypeExtraViewAdapter(View headerView, View footerView) {
 14         this.headerView = headerView;
 15         this.footerView = footerView;
 16         extraCount += hasHeaderView() ? 0 : 1;
 17         extraCount += hasFooterView() ? 0 : 1;
 18     }
 19 
 20     public boolean hasHeaderView() {
 21         return null != headerView;
 22     }
 23 
 24     public boolean hasFooterView() {
 25         return null != footerView;
 26     }
 27 
 28     public int innerPositionToRealItemPosition(int innerPosition) {
 29         return hasHeaderView() ? innerPosition - 1 : innerPosition;
 30     }
 31 
 32     @TargetApi(Build.VERSION_CODES.DONUT)
 33     @Override
 34     public ABRecyclerViewTypeExtraHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
 35         ABAdapterTypeRender<ABRecyclerViewTypeExtraHolder> render = getAdapterTypeRender(viewType);
 36         ABRecyclerViewTypeExtraHolder holder = render.getReusableComponent();
 37         holder.itemView.setTag(R.id.ab__id_adapter_item_type_render, render);
 38         render.fitEvents();
 39         return holder;
 40     }
 41 
 42     @TargetApi(Build.VERSION_CODES.DONUT)
 43     @Override
 44     public void onBindViewHolder(ABRecyclerViewTypeExtraHolder holder, int innerPosition) {
 45         ABAdapterTypeRender<ABRecyclerViewTypeExtraHolder> render = (ABAdapterTypeRender<ABRecyclerViewTypeExtraHolder>) holder.itemView.getTag(R.id.ab__id_adapter_item_type_render);
 46         /**
 47          * 計算該item在list中的index(不包括headerView和footerView)
 48          */
 49         int realItemPosition = innerPositionToRealItemPosition(innerPosition);
 50         render.fitDatas(realItemPosition);
 51         /**
 52          * 從新設置item在list中的index(不包括headerView和footerView)
 53          */
 54         holder.setRealItemPosition(realItemPosition);
 55     }
 56 
 57     /**
 58      * 經過類型得到對應的render(不包括headerView和footerView)
 59      *
 60      * @param type
 61      * @return
 62      */
 63     public abstract ABAdapterTypeRender<ABRecyclerViewTypeExtraHolder> getAdapterTypeRenderExcludeExtraView(int type);
 64 
 65     /**
 66      * 獲取item的數量(不包括headerView和footerView)
 67      *
 68      * @return
 69      */
 70     public abstract int getItemCountExcludeExtraView();
 71 
 72     /**
 73      * 經過realItemPosition獲得該item的類型(不包括headerView和footerView)
 74      *
 75      * @param realItemPosition
 76      * @return
 77      */
 78     public abstract int getItemViewTypeExcludeExtraView(int realItemPosition);
 79 
 80     public ABAdapterTypeRender<ABRecyclerViewTypeExtraHolder> getAdapterTypeRender(int type) {
 81         switch (type) {
 82             case TYPE_HEADER_VIEW:
 83                 return new ABRecyclerViewTypeExtraRender(headerView);
 84             case TYPE_FOOTER_VIEW:
 85                 return new ABRecyclerViewTypeExtraRender(footerView);
 86             default:
 87                 return getAdapterTypeRenderExcludeExtraView(type);
 88         }
 89     }
 90 
 91     @Override
 92     public int getItemCount() {
 93         return getItemCountExcludeExtraView() + extraCount;
 94     }
 95 
 96     @Override
 97     public int getItemViewType(int innerPosition) {
 98         if (null != headerView && 0 == innerPosition) { // header
 99             return TYPE_HEADER_VIEW;
100         } else if (null != footerView && getItemCount() - 1 == innerPosition) { // footer
101             return TYPE_FOOTER_VIEW;
102         }
103         return getItemViewTypeExcludeExtraView(innerPositionToRealItemPosition(innerPosition));
104     }
105 }

如上述代碼所示:

由於咱們的需求是須要添加「加載進度條」,因此須要像ListView那樣,添加一個FooterView。但是坑爹的是,RecyclerView不提供addheaderView()和addFooterView()方法,因此只能咱們本身去實現了,方法固然是使用不一樣type來區分類型。雖然headerView這裏沒有用到,可是也順帶實現下好了。

這裏咱們使用的Holder是ABRecyclerViewTypeExtraHolder,這個類待會分析。

headerView和footerView這裏使用構造方法傳入,並緩存headerView和footerView。在onCreateViewHolder中,咱們經過不一樣type,生成相應的render。並把render綁定到holder的itemView上面,由於既然如今複用的是holder,那個人render也要實現複用的話,也綁定在holder裏面吧。而後調用render的fitEvents方法,來實現render裏面的事件綁定。

onBindViewHolder方法中,經過holder,取出render,而後注意Line49~54,裏面執行了innerPositionToRealItemPosition()方法對innerPosition到RealItemPosition的轉換,這裏的innerPosition表明內部的position,由於這裏可能會添加了headerView,一旦添加了headerView,那position跟數據源List中的index就不匹配了,這樣的話綁定點擊事件後,經過holder.getPositon()獲得的position就不是index了,因此不能寫「list.get(position)...」了。這裏的realItemPosition就表明數據源對應的index 。因此咱們要把innerPosition轉換爲realItemPosition,方法很簡單,innerPosition-1=realItemPosition(這裏的減去1實際上就是減去了headerView)。其實holder中原本就緩存了當前使用了這個holder的item的position,可是由於有了headerView的存在,position就不等於realItemPosition了,因此咱們還須要緩存realItemPosition。因此代碼Line46~54誕生了。

getItemCountExcludeExtraView()方法須要子類實現,返回數據源中的數據數量,而後再加上extraCount便是getItemCount的值。

getItemViewType()方法先執行了header類型和footer類型的邏輯,而後再讓自類去實現getItemViewTypeExcludeExtraView()來執行其餘類型的邏輯。

至於ABRecyclerViewTypeExtraRenderhttps://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/support/recyclerview/adapter/extra/ABRecyclerViewTypeExtraRender.java

部分的實現能夠查看

[Android]使用AdapterTypeRender對不一樣類型的item數據到UI的渲染http://www.cnblogs.com/tiantianbyconan/p/3992843.html

實現的原理大同小異了。

 

而後分析下ABRecyclerViewTypeExtraHolderhttps://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/support/recyclerview/adapter/extra/ABRecyclerViewTypeExtraHolder.java這個類,代碼以下:

 1 /**
 2  * Author: wangjie
 3  * Email: tiantian.china.2@gmail.com
 4  * Date: 1/22/15.
 5  */
 6 public class ABRecyclerViewTypeExtraHolder extends ABRecyclerViewHolder {
 7     public ABRecyclerViewTypeExtraHolder(View itemView) {
 8         super(itemView);
 9     }
10 
11     /**
12      * 保存當前position(list index,不包括headerView和footerView)
13      */
14     private int realItemPosition;
15 
16     public int getRealItemPosition() {
17         return realItemPosition;
18     }
19 
20     protected void setRealItemPosition(int realItemPosition) {
21         this.realItemPosition = realItemPosition;
22     }
23 
24 }

它繼承了ABRecyclerViewHolderhttps://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/support/recyclerview/adapter/ABRecyclerViewHolder.java),只是保存了一個剛剛講到的realItemPosition對象。

因此咱們再貼下ABRecyclerViewHolder的代碼:

 1 /**
 2  * Author: wangjie
 3  * Email: tiantian.china.2@gmail.com
 4  * Date: 1/19/15.
 5  */
 6 public class ABRecyclerViewHolder extends RecyclerView.ViewHolder {
 7     private static final String TAG = ABRecyclerViewHolder.class.getSimpleName();
 8     private SparseArray<View> holder = null;
 9 
10     public ABRecyclerViewHolder(View itemView) {
11         super(itemView);
12     }
13 
14     /**
15      * 獲取一個緩存的view
16      *
17      * @param id
18      * @param <T>
19      * @return
20      */
21     public <T extends View> T obtainView(int id) {
22         if (null == holder) {
23             holder = new SparseArray<>();
24         }
25         View view = holder.get(id);
26         if (null != view) {
27             return (T) view;
28         }
29         view = itemView.findViewById(id);
30         if (null == view) {
31             Logger.e(TAG, "no view that id is " + id);
32             return null;
33         }
34         holder.put(id, view);
35         return (T) view;
36     }
37 
38     /**
39      * 獲取一個緩存的view,並自動轉型
40      *
41      * @param id
42      * @param <T>
43      * @return
44      */
45     public <T> T obtainView(int id, Class<T> viewClazz) {
46         View view = obtainView(id);
47         if (null == view) {
48             return null;
49         }
50         return (T) view;
51     }
52 
53 }

而後發現,它的做用是在於使用SparseArray來緩存findViewById後的控件。這樣作的好處是,這個holder能夠適用於任何的RecyclerView.Adapter中。只要經過obtainView()方法就能獲得itemView中的具體的view對象,以下代碼所示:

1 Person person = adapter.getList().get(position);
2 holder.obtainView(R.id.recycler_view_test_item_person_name_tv, TextView.class).setText(person.getName());=
3 holder.obtainView(R.id.recycler_view_test_item_person_age_tv, TextView.class).setText(person.getAge() + "歲");

 

示例代碼:

https://github.com/wangjiegulu/RecyclerViewSample

 

[Android]使用RecyclerView替代ListView(一)

http://www.cnblogs.com/tiantianbyconan/p/4232560.html

 

[Android]使用RecyclerView替代ListView(三) 

http://www.cnblogs.com/tiantianbyconan/p/4268097.html

相關文章
相關標籤/搜索