如下內容爲原創,轉載請註明: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()來執行其餘類型的邏輯。
至於ABRecyclerViewTypeExtraRender(https://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)
實現的原理大同小異了。
而後分析下ABRecyclerViewTypeExtraHolder(https://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 }
它繼承了ABRecyclerViewHolder(https://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