[Android]使用RecyclerView替代ListView(二)
2015-01-22 19:51
579 查看
以下内容为原创,转载请注明:
来自天天博客:/article/4917491.html
以前写过一篇“[Android]使用AdapterTypeRender对不同类型的item数据到UI的渲染(/article/4917483.html)”,用于在有很多不同类型不同布局的item的时候怎么去较好的进行view的绑定和数据的渲染,但是这个是针对ListView写的。这次我针对RecyclerView也重新实现了一遍。
接下来演示下怎么去渲染不同类型的item,并且使它支持下拉刷新,滚动到底部显示加载进度条显示。
以下所有的封装都在AndroidBucket项目中:https://github.com/wangjiegulu/AndroidBucket/tree/master/src/com/wangjie/androidbucket/support/recyclerview
使用的方式如下:
如上述代码:
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:设置滑动到顶部或底部的监听器,然后一旦滑动到底部则加载更多数据。
Line36~45:也是设置滑动监听器,滑动过程中如果不是处在第一个item,如果是,则就可以下拉使用SwipeRefreshLayout进行刷新,如果不是则,仅用SwipeRefreshLayout。之所以需要做这个处理,是因为Google事件处理的一个bug--。
Line50:使用了一个PersonTypeAdapter,这个类继承了ABRecyclerViewTypeExtraViewAdapter,这个类继承RecyclerView.Adapter实现了对不同type渲染数据的封装。
接下来重点分析下ABRecyclerViewTypeExtraViewAdapter这个类(这个类在平常使用时不需要关注):
如上述代码所示:
因为我们的需求是需要添加“加载进度条”,所以需要像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)
部分的实现可以查看
使用AdapterTypeRender对不同类型的item数据到UI的渲染][Android]使用AdapterTypeRender对不同类型的item数据到UI的渲染(/article/4917483.html)
实现的原理大同小异了。
然后分析下ABRecyclerViewTypeExtraHolder(https://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/support/recyclerview/adapter/extra/ABRecyclerViewTypeExtraHolder.java)这个类,代码如下:
它继承了ABRecyclerViewHolder(https://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/support/recyclerview/adapter/ABRecyclerViewHolder.java),只是保存了一个刚刚讲到的realItemPosition对象。
所以我们再贴下ABRecyclerViewHolder的代码:
然后发现,它的作用是在于使用SparseArray来缓存findViewById后的控件。这样做的好处是,这个holder可以适用于任何的RecyclerView.Adapter中。只要通过obtainView()方法就能得到itemView中的具体的view对象,如下代码所示:
示例代码:
https://github.com/wangjiegulu/RecyclerViewSample
[Android]使用RecyclerView替代ListView(一):
/article/4917490.html
[Android]使用RecyclerView替代ListView(三):
/article/4917492.html
来自天天博客:/article/4917491.html
以前写过一篇“[Android]使用AdapterTypeRender对不同类型的item数据到UI的渲染(/article/4917483.html)”,用于在有很多不同类型不同布局的item的时候怎么去较好的进行view的绑定和数据的渲染,但是这个是针对ListView写的。这次我针对RecyclerView也重新实现了一遍。
接下来演示下怎么去渲染不同类型的item,并且使它支持下拉刷新,滚动到底部显示加载进度条显示。
以下所有的封装都在AndroidBucket项目中:https://github.com/wangjiegulu/AndroidBucket/tree/master/src/com/wangjie/androidbucket/support/recyclerview
使用的方式如下:
final View footerView = LayoutInflater.from(context).inflate(R.layout.recycler_view_item_type_footer, null); // 不知道为什么在xml设置的“android:layout_width="match_parent"”无效了,需要在这里重新设置 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); footerView.setLayoutParams(lp); recyclerView.setHasFixedSize(true); final ABaseLinearLayoutManager layoutManager = new ABaseLinearLayoutManager(context); layoutManager.setOnRecyclerViewScrollLocationListener(recyclerView, new OnRecyclerViewScrollLocationListener() { @Override public void onTopWhenScrollIdle(RecyclerView recyclerView) { Logger.d(TAG, "onTopWhenScrollIdle..."); } @Override public void onBottomWhenScrollIdle(RecyclerView recyclerView) { Logger.d(TAG, "onBottomWhenScrollIdle..."); footerView.setVisibility(View.VISIBLE); ThreadPool.go(new Runtask<Object, Object>() { @Override public Object runInBackground() { ABThreadUtil.sleep(3000); return null; } @Override public void onResult(Object result) { super.onResult(result); refreshLayout.setRefreshing(false); footerView.setVisibility(View.GONE); } }); } }); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); layoutManager.getRecyclerViewScrollManager().addScrollListener(recyclerView, new OnRecyclerViewScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { refreshLayout.setEnabled(layoutManager.findFirstCompletelyVisibleItemPosition() == 0); } }); recyclerView.setLayoutManager(layoutManager); initData(); adapter = new PersonTypeAdapter(context, personList, null, footerView); adapter.setOnRecyclerViewListener(this); recyclerView.setAdapter(adapter); refreshLayout.setColorSchemeColors(Color.BLUE, Color.RED, Color.YELLOW, Color.GREEN); refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { ThreadPool.go(new Runtask<Object, Object>() { @Override public Object runInBackground() { ABThreadUtil.sleep(3000); return null; } @Override public void onResult(Object result) { super.onResult(result); refreshLayout.setRefreshing(false); footerView.setVisibility(View.GONE); } }); } });
如上述代码:
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:设置滑动到顶部或底部的监听器,然后一旦滑动到底部则加载更多数据。
Line36~45:也是设置滑动监听器,滑动过程中如果不是处在第一个item,如果是,则就可以下拉使用SwipeRefreshLayout进行刷新,如果不是则,仅用SwipeRefreshLayout。之所以需要做这个处理,是因为Google事件处理的一个bug--。
Line50:使用了一个PersonTypeAdapter,这个类继承了ABRecyclerViewTypeExtraViewAdapter,这个类继承RecyclerView.Adapter实现了对不同type渲染数据的封装。
接下来重点分析下ABRecyclerViewTypeExtraViewAdapter这个类(这个类在平常使用时不需要关注):
/** * Author: wangjie * Email: tiantian.china.2@gmail.com * Date: 1/22/15. */ public abstract class ABRecyclerViewTypeExtraViewAdapter extends RecyclerView.Adapter<ABRecyclerViewTypeExtraHolder> { private static final int TYPE_HEADER_VIEW = 0x7683; private View headerView; private static final int TYPE_FOOTER_VIEW = 0x7684; private View footerView; private int extraCount; protected ABRecyclerViewTypeExtraViewAdapter(View headerView, View footerView) { this.headerView = headerView; this.footerView = footerView; extraCount += hasHeaderView() ? 0 : 1; extraCount += hasFooterView() ? 0 : 1; } public boolean hasHeaderView() { return null != headerView; } public boolean hasFooterView() { return null != footerView; } public int innerPositionToRealItemPosition(int innerPosition) { return hasHeaderView() ? innerPosition - 1 : innerPosition; } @TargetApi(Build.VERSION_CODES.DONUT) @Override public ABRecyclerViewTypeExtraHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { ABAdapterTypeRender<ABRecyclerViewTypeExtraHolder> render = getAdapterTypeRender(viewType); ABRecyclerViewTypeExtraHolder holder = render.getReusableComponent(); holder.itemView.setTag(R.id.ab__id_adapter_item_type_render, render); render.fitEvents(); return holder; } @TargetApi(Build.VERSION_CODES.DONUT) @Override public void onBindViewHolder(ABRecyclerViewTypeExtraHolder holder, int innerPosition) { ABAdapterTypeRender<ABRecyclerViewTypeExtraHolder> render = (ABAdapterTypeRender<ABRecyclerViewTypeExtraHolder>) holder.itemView.getTag(R.id.ab__id_adapter_item_type_render); /** * 计算该item在list中的index(不包括headerView和footerView) */ int realItemPosition = innerPositionToRealItemPosition(innerPosition); render.fitDatas(realItemPosition); /** * 重新设置item在list中的index(不包括headerView和footerView) */ holder.setRealItemPosition(realItemPosition); } /** * 通过类型获得对应的render(不包括headerView和footerView) * * @param type * @return */ public abstract ABAdapterTypeRender<ABRecyclerViewTypeExtraHolder> getAdapterTypeRenderExcludeExtraView(int type); /** * 获取item的数量(不包括headerView和footerView) * * @return */ public abstract int getItemCountExcludeExtraView(); /** * 通过realItemPosition得到该item的类型(不包括headerView和footerView) * * @param realItemPosition * @return */ public abstract int getItemViewTypeExcludeExtraView(int realItemPosition); public ABAdapterTypeRender<ABRecyclerViewTypeExtraHolder> getAdapterTypeRender(int type) { switch (type) { case TYPE_HEADER_VIEW: return new ABRecyclerViewTypeExtraRender(headerView); case TYPE_FOOTER_VIEW: return new ABRecyclerViewTypeExtraRender(footerView); default: return getAdapterTypeRenderExcludeExtraView(type); } } @Override public int getItemCount() { return getItemCountExcludeExtraView() + extraCount; } @Override public int getItemViewType(int innerPosition) { if (null != headerView && 0 == innerPosition) { // header return TYPE_HEADER_VIEW; } else if (null != footerView && getItemCount() - 1 == innerPosition) { // footer return TYPE_FOOTER_VIEW; } return getItemViewTypeExcludeExtraView(innerPositionToRealItemPosition(innerPosition)); } }
如上述代码所示:
因为我们的需求是需要添加“加载进度条”,所以需要像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)
部分的实现可以查看
使用AdapterTypeRender对不同类型的item数据到UI的渲染][Android]使用AdapterTypeRender对不同类型的item数据到UI的渲染(/article/4917483.html)
实现的原理大同小异了。
然后分析下ABRecyclerViewTypeExtraHolder(https://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/support/recyclerview/adapter/extra/ABRecyclerViewTypeExtraHolder.java)这个类,代码如下:
/** * Author: wangjie * Email: tiantian.china.2@gmail.com * Date: 1/22/15. */ public class ABRecyclerViewTypeExtraHolder extends ABRecyclerViewHolder { public ABRecyclerViewTypeExtraHolder(View itemView) { super(itemView); } /** * 保存当前position(list index,不包括headerView和footerView) */ private int realItemPosition; public int getRealItemPosition() { return realItemPosition; } protected void setRealItemPosition(int realItemPosition) { this.realItemPosition = realItemPosition; } }
它继承了ABRecyclerViewHolder(https://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/support/recyclerview/adapter/ABRecyclerViewHolder.java),只是保存了一个刚刚讲到的realItemPosition对象。
所以我们再贴下ABRecyclerViewHolder的代码:
/** * Author: wangjie * Email: tiantian.china.2@gmail.com * Date: 1/19/15. */ public class ABRecyclerViewHolder extends RecyclerView.ViewHolder { private static final String TAG = ABRecyclerViewHolder.class.getSimpleName(); private SparseArray<View> holder = null; public ABRecyclerViewHolder(View itemView) { super(itemView); } /** * 获取一个缓存的view * * @param id * @param <T> * @return */ public <T extends View> T obtainView(int id) { if (null == holder) { holder = new SparseArray<>(); } View view = holder.get(id); if (null != view) { return (T) view; } view = itemView.findViewById(id); if (null == view) { Logger.e(TAG, "no view that id is " + id); return null; } holder.put(id, view); return (T) view; } /** * 获取一个缓存的view,并自动转型 * * @param id * @param <T> * @return */ public <T> T obtainView(int id, Class<T> viewClazz) { View view = obtainView(id); if (null == view) { return null; } return (T) view; } }
然后发现,它的作用是在于使用SparseArray来缓存findViewById后的控件。这样做的好处是,这个holder可以适用于任何的RecyclerView.Adapter中。只要通过obtainView()方法就能得到itemView中的具体的view对象,如下代码所示:
Person person = adapter.getList().get(position); holder.obtainView(R.id.recycler_view_test_item_person_name_tv, TextView.class).setText(person.getName());= holder.obtainView(R.id.recycler_view_test_item_person_age_tv, TextView.class).setText(person.getAge() + "岁");
示例代码:
https://github.com/wangjiegulu/RecyclerViewSample
[Android]使用RecyclerView替代ListView(一):
/article/4917490.html
[Android]使用RecyclerView替代ListView(三):
/article/4917492.html
相关文章推荐
- 「Android」使用RecyclerView替代ListView
- android RecyclerView 使用完全解析 替代ListView(一)
- Android RecyclerView 使用解析,替代ListView
- [Android]使用RecyclerView替代ListView(一)
- [Android]使用RecyclerView替代ListView(一)
- [Android]使用RecyclerView替代ListView(一)
- [Android]使用RecyclerView替代ListView(一)
- android将替代ListView的RecyclerView的使用和进阶使用,替用GallRery
- [Android]使用RecyclerView替代ListView(一)
- Android使用RecyclerView替代ListView(一)
- [Android]使用RecyclerView替代ListView(一)
- [Android]使用RecyclerView替代ListView(三)
- [Android]使用RecyclerView替代ListView(四:SeizeRecyclerView)
- Android App开发中使用RecyclerView替代ListView的实践
- [Android]使用RecyclerView替代ListView
- [Android]使用RecyclerView替代ListView(二)
- [Android]使用RecyclerView替代ListView(三)
- [Android]使用RecyclerView替代ListView(三)
- [Android]使用RecyclerView替代ListView(一)
- [Android]使用RecyclerView替代ListView(一)