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

来自天天博客: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

 

使用的方式如下:

 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()来执行其他类型的逻辑。

至于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这个类,代码如下:

 /**
* 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;
} }

它继承了ABRecyclerViewHolderhttps://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(一)

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

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

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

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

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

    以下内容为原创,转载请注明: 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4268097.html  这次来使用RecyclerView实现Pinn ...

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

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4232560.html RecyclerView是一个比List ...

  3. [Android]使用RecyclerView替代ListView(四:SeizeRecyclerView)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:<> [Android]使用RecyclerView替代ListView(四:SeizeRecyclerView) 在RecyclerV ...

  4. Android笔记——RecyclerView替代ListView

    ListView是常用列表控件,但设置Adapter时自定义代码较为复杂,因此Android3.0后,增加RecyclerView替代ListView RecyclerView没有提供OnItemCl ...

  5. Android用RecyclerView实现的二维Excel效果组件

    excelPanel 二维RecyclerView.不仅可以加载历史数据,而且可以加载未来的数据.   包括在您的项目中 excelPanel 二维RecyclerView.不仅可以加载历史数据,而且 ...

  6. Android最新组件RecyclerView,替代ListView

    转载请注明出处:http://blog.csdn.net/allen315410/article/details/40379159 万众瞩目的android最新5.0版本号不久前已经正式公布了,对于我 ...

  7. Android RecyclerView与ListView比较

    RecyclerView 概述 RecyclerView 集成自 ViewGroup .RecyclerView是Android-support-V7版本中新增的一个Widgets,官方对于它的介绍是 ...

  8. 浅谈RecyclerView(完美替代ListView,GridView)

    Android RecyclerView 是Android5.0推出来的,导入support-v7包即可使用. 个人体验来说,RecyclerView绝对是一款功能强大的控件. 首先总结下Recycl ...

  9. Android控件RecyclerView与ListView的异同

    在我的一篇介绍Android新控件RecyclerView的博客(Android L新控件RecyclerView简介)中,一个读者留言说RecyclerView跟ListView之间好像没有什么不同 ...

随机推荐

  1. Azure Redis Cache (2) 创建和使用Azure Redis Cache

    <Windows Azure Platform 系列文章目录> 本文介绍的是国内由世纪互联运维的Azure China. 注意: 截至今日2015年10月7日,国内由世纪互联运维的Azur ...

  2. LoRaWAN协议(五)--OTAA入网方式详述

    前言 OTAA(Over-The-Air Activation),是LoRaWAN的一种空中入网方式.当node在上电的时候处于非入网状态时,需要先入网才能和服务器进行通信.其操作就是node发送jo ...

  3. TCP/IP详解学习笔记(13)-TCP坚持定时器,TCP保活定时器

    TCP一共有四个主要的定时器,前面已经讲到了一个--超时定时器--是TCP里面最复杂的一个,另外的三个是: 坚持定时器 保活定时器 2MSL定时器 其中坚持定时器用于防止通告窗口为0以后双方互相等待死 ...

  4. postgres中的中文分词zhparser

    postgres中的中文分词zhparser postgres中的中文分词方法 基本查了下网络,postgres的中文分词大概有两种方法: Bamboo zhparser 其中的Bamboo安装和使用 ...

  5. 【视频处理】YUV格式说明

    YUV,是一种颜色编码方法,Y表示明亮度(Luminance.Luma),U和V则是色度.浓度(Chrominance.Chroma). YUV,Y`UV,YCbCr,YPbPr等都可以称为YUV,彼 ...

  6. WinForm 简单蒙版实现控件遮盖

    在Web上面要实现一个遮罩层或者说是蒙版吧,有了DIV那不算什么难事,只要给div定好位置和大小,把颜色的Alpha值设一下就有透明的效果.不过在Winform中实现起来就没那么简单了事.尝试过用一个 ...

  7. Win10怎么输入法切换

    按 windows 键+空格键,或者CTRL+Shift,或者只按 Shift,或者CTRL+空格 试试 方法/步骤1在桌面上点击[控制面板],进入控制面板后使用分类显示控制面板内的选项.然后在语言下 ...

  8. Firemonkey 载入 Style 皮肤 (*.fsf 二进制文件) 速度测试

    说明:Firemonkey 可以换肤是一大亮点,但使用它必须要付出一点代价,就是需要一点载入的时间,下面以 *.fsf 二进制文件来做载入测试,有兴趣可以参考看看. 开发:XE8 for iOS 皮肤 ...

  9. DevExpress Ribbongallerybaritem选择性皮肤重组

    void InitSkinGallery() () { SkinHelper skinHelper = new SkinHelper(); RibbonControl masterRibbonCont ...

  10. Scalaz(55)- scalaz-stream: fs2-基础介绍,fs2 stream transformation

    fs2是scalaz-stream的最新版本,沿用了scalaz-stream被动式(pull model)数据流原理但采用了全新的实现方法.fs2比较scalaz-stream而言具备了:更精简的基 ...