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

以下内容为原创,转载请注明:

来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4242541.html

以前写过一篇“[Android]使用AdapterTypeRender对不同类型的item数据到UI的渲染http://www.cnblogs.com/tiantianbyconan/p/3992843.html)”,用于在有很多不同类型不同布局的item的时候怎么去较好的进行view的绑定和数据的渲染,但是这个是针对ListView写的。这次我针对RecyclerView也重新实现了一遍。

接下来演示下怎么去渲染不同类型的item,并且使它支持下拉刷新,滚动到底部显示加载进度条显示。

以下所有的封装都在AndroidBucket项目中:https://github.com/wangjiegulu/AndroidBucket/tree/master/src/com/wangjie/androidbucket/support/recyclerview

 

使用的方式如下:

 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         });

如上述代码:

Line1:从布局文件中inflate出一个View实例,这个View实例,下面会被用于作为下载提示的footer。

Line8:生成一个ABaseLinearLayoutManager实例,显然这个类是继承LinearLayoutManager之后扩展的,详见:https://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/support/recyclerview/layoutmanager/ABaseLinearLayoutManager.java,这个类对滑动的监听进行了扩展,可以监听滑动到顶部活着底部的事件

Line9~34:设置滑动到顶部或底部的监听器,然后一旦滑动到底部使用SwipeRefreshLayout来刷新。

Line36~45:也是设置滑动监听器,滑动过程中如果不是处在第一个item,如果是,则就可以下拉使用SwipeRefreshLayout进行刷新,如果不是则,仅用SwipeRefreshLayout。之所以需要做这个处理,是因为Google事件处理的一个bug--。

Line50:使用了一个PersonTypeAdapter,这个类继承了ABRecyclerViewTypeExtraViewAdapter这个类继承RecyclerView.Adapter实现了对不同type渲染数据的封装。

接下来重点分析下ABRecyclerViewTypeExtraViewAdapter这个类(这个类在平常使用时不需要关注):

  1 /**
  2  * Author: wangjie
  3  * Email: [email protected]
  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: [email protected]
 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: [email protected]
 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() + "岁");
时间: 2024-10-01 21:20:49

[Android]使用RecyclerView替代ListView(二)的相关文章

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

以下内容为原创,转载请注明: 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4268097.html  这次来使用RecyclerView实现PinnedListView的效果,效果很常见: 开发的代码建立在上一篇([Android]使用RecyclerView替代ListView(二):http://www.cnblogs.com/tiantianbyconan/p/4242541.html)基础之上. 修改布局如下: 1 <?xml vers

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

以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4232560.html RecyclerView是一个比ListView更灵活的一个控件,以后可以直接抛弃ListView了.具体好在哪些地方,往下看就知道了. 首先我们来使用RecyclerView来实现ListView的效果,一个滚动列表,先看下效果图(除了有动画之外,没什么特别--): 每个item的布局如下: <?xml version="1.0&

使用RecyclerView替代ListView(一)

RecyclerView是一个比ListView更灵活的一个控件,以后可以直接抛弃ListView了.具体好在哪些地方,往下看就知道了. 首先我们来使用RecyclerView来实现ListView的效果,一个滚动列表,先看下效果图(除了有动画之外,没什么特别--): 每个item的布局如下: <?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http:/

Android最新组件RecyclerView,替代ListView

转载请注明出处:http://blog.csdn.net/allen315410/article/details/40379159 万众瞩目的android最新5.0版本号不久前已经正式公布了,对于我这样对新事物不感冒的人来说,自然也是会关注的,除了新的android5.0带来的新的UI设计和用户体验之外,最让android程序猿感兴趣的是5.0版本号的sdk和一大堆新的API.5.0据说是额外添加或者改动了5000个API,新增了一些新的组件,以下介绍的RecyclerView就是当中之中的一

将替代ListView的RecyclerView 的使用(一)

RecyclerView 是 android-support-v7-21 版本中新增的一个 Widgets, 还有一个 CardView 会在下次介绍使用.官方介绍 RecyclerView 是 ListView 的升级版本,更加先进和灵活.我们写一个简单的实例例,来看一下究竟有多先进和灵活. build.gradle 配置 android { compileSdkVersion 'android-L' buildToolsVersion "20.0.0" defaultConfig

Android中RecyclerView嵌套RecyclerView或嵌套ListView

Android中RecyclerView嵌套RecyclerView或嵌套ListView

如何使用Android最新的RecyclerView取代ListView?

效果图如下: 使用RecyclerView之前需要先导入android.support.v7.widget.RecyclerView所在的jar包.就在support.v7下面,目录结构如下: ...\android-sdk-windows\extras\android\support\v7\recyclerview\libs\android-support-v7-recyclerview.jar 找到自己的SDK目录,按照上述路径找到android-support-v7-recyclervie

Android学习笔记(四二):SQLite、ListView、ContextMenu

继续上一个例子,结合ListView中对SQLite进行操作. 通过CursorAdapter在ListView中的数据呈现 在上一个例子中,我们可以对SQLite中的数据库进行增删改查,将数据读到游标Cursor中,然后一一读出.在Android中可以通过CursorAdapter直接将数据映射到ListView中,如下处理: public class Chapter22Test1 extends ListActivity{    private SQLiteDatabase  db = nu

Android之RecyclerView(一)

概述 RecyclerView 是一个 ViewGroup,它用于渲染任何基于适配器的 View.它被官方定义为 ListView 和 GridView 的取代者,是在 Support V7 包中引入的.使用该组件的一个理由是:它有一个更易于扩展的框架,尤其是它提供了横向和纵向两个方向滚动的能力.当数据集合根据用户的操作或网络状态的变化而变化时,你很需要这个控件. 要使用 RecyclerView,需要以下的几个元素: RecyclerView.Adapter :使用RecyclerView之前