RecyclerView更通用——listView的onItemClick,onLongItemClick,addHeaderView,addFooterView
一、点击事件
setOnItemClickListener,setOnItemLongClickListener
RecyclerView
中虽然没有提供上面这两个接口,但是给我们提供了另外一个接口:OnItemTouchListener
看这个接口的文档描述我们知道此接口可以对RecyclerView
中的手势进行监听处理,因此我们可以采用OnItemTouchListener+GestureDetector
来实现RecyclerView
的OnItemClick
和OnItemLongClick
。实现方式也比较简单,还是上代码吧
private
OnItemClickListener mOnItemClickListener;
private
OnItemLongClickListener mItemLongClickListener;
mGestureDetector =
new
GestureDetector(context,
new
GestureDetector.SimpleOnGestureListener() {
@Override
public
void
onLongPress(MotionEvent e) {
super
.onLongPress(e);
if
(mItemLongClickListener !=
null
) {
View childView = findChildViewUnder(e.getX(), e.getY());
if
(childView !=
null
) {
int
position = getChildLayoutPosition(childView);
mItemLongClickListener.onItemLongClick(position, childView);
}
}
}
@Override
public
boolean
onSingleTapUp(MotionEvent e) {
if
(mOnItemClickListener !=
null
) {
View childView = findChildViewUnder(e.getX(),e.getY());
if
(childView !=
null
){
int
position = getChildLayoutPosition(childView);
mOnItemClickListener.onItemClick(position, childView);
return
true
;
}
}
return
super
.onSingleTapUp(e);
}
});
addOnItemTouchListener(
new
SimpleOnItemTouchListener() {
@Override
public
boolean
onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
if
(mGestureDetector.onTouchEvent(e)) {
//交由手势处理
return
true
;
}
return
false
;
}
});
/**
* Item项点击事件
*/
public
interface
OnItemClickListener {
void
onItemClick(
int
position, View itemView);
}
/**
* Item项长按点击事件
*/
public
interface
OnItemLongClickListener {
void
onItemLongClick(
int
position, View itemView);
}
二、addHeaderView,addFooterView
实现原理
前面写过一篇文章RecyclerView
下拉刷新上拉加载 介绍过RecyclerView
的上拉加载的实现方式,里面的上拉加载进度条其实也是RecyclerView
的一个FooterView
,其实现方式就是为LoadMoreView
设置了一个特殊的ItemViewType
来进行区分展示,因此我这里的HeaderView
和FooterView
也是通过为它们设置不同的ItemViewType
来进行区分展示。
我们知道ListView
中的addHeaderView
和addFooterView
都是可以添加多个View的,也就是说RecyclerView
中也会出现添加多个完全不同的HeaderView
或FooterView
,所以我们必须为添加的每个HeaderView
和FooterView
都设置一个ItemViewType
从而达到添加多个不同的HeaderView
或FooterView
的目的(如果所有的HeaderView
或FooterView
都设置同一个ItemViewType
的话只能显示一种View
的HeaderView
或FooterView
)。
实现步骤
知道了实现原理,我们再来理一下实现步骤:
- 因为每个
HeaderView
或FooterView
都需要对应一个ItemViewType
,所以我们需要分别为它们建立一个映射关系,我采用SparseArray
实现映射 - 我们需要在添加
HeaderView
或FooterView
的时候生成对应的ItemViewType
值,也就是我们需要定义一个ItemViewType
的生成规则,我采用了基准值+视图个数的方式生成ItemViewType
- 自定义一个
Adapter
继承自RecyclerView.Adapter
,重写里面的几个方法:onCreateViewHolder,onBindViewHolder,getItemViewType,getItemCount
getItemCount
方法中返回的数据总数显然是:HeaderView总数
+FooterView总数
+List列表展示的数据总数
- 重写
onBindViewHolder
,getItemViewType
这两个方法时,显然需要根据position
判断当前位置是否为HeaderView
或是FooterView
,而根据展示顺序来看当0<=position<HeaderView总数
时是HeaderView
,而当position>=(HeaderView总数+List总数)
时则是FooterView
,其余位置则是List
数据对应的View
- 而重写
onCreateViewHolder
方法时,则可用直接根据其方法参数viewType
在SparseArray映射中查找是否存在该类型的HeaderView
或是FooterView
,有则返回,没有则返回List数据展示的View
//HeaderView的ItemViewType的生成基准值,生成规则为基准值+当前HeaderView的个数
private
static
final
int
TYPE_HEADER =
100000
;
//FooterView的ItemViewType的生成基准值,生成规则为基准值+当前的FooterView的个数
private
static
final
int
TYPE_FOOTER =
200000
;
//存储HeaderView,key值作为对应HeaderView的ItemViewType
private
SparseArray<view> mHeaderViews =
new
SparseArray<>(
0
);
//存储FooterView,key值作为对应HeaderView的ItemViewType
private
SparseArray<view> mFooterViews =
new
SparseArray<>(
0
);
@Override
public
final
ViewHolder onCreateViewHolder(ViewGroup parent,
int
viewType) {
if
(isHeaderViewEnable() && mHeaderViews.get(viewType) !=
null
) {
return
new
ViewHolder(mHeaderViews.get(viewType));
}
else
if
(isFooterViewEnable() && mFooterViews.get(viewType) !=
null
) {
return
new
ViewHolder(mFooterViews.get(viewType));
}
return
onCreateItemViewHolder(parent, viewType);
}
@Override
public
final
void
onBindViewHolder(ViewHolder holder,
int
position) {
if
(isFooterView(position) || isHeaderView(position)) {
return
;
}
T item = getItem(position - getHeaderViewCount());
onBindItemViewHolder(holder, position, item);
}
@Override
public
final
int
getItemViewType(
int
position) {
if
(isHeaderView(position)) {
//FooterView
return
mHeaderViews.keyAt(position);
}
if
(isFooterView(position)){
//HeaderView
return
mFooterViews.keyAt(position - getHeaderViewCount() - getItemDataCount());
}
return
getItemViewTypeForData(position);
}
/**
* 展示的总数据数(包括HeaderView和FooterView)
*
* @return
*/
@Override
public
final
int
getItemCount() {
//从写此方法,数据总数需要包括HeaderView总数和FooterView总数
return
getItemDataCount() + getHeaderViewCount() + getFooterViewCount();
}
/**
* 要展示的有效数据数(不包括HeaderView和FooterView)
*
* @return
*/
public
int
getItemDataCount() {
return
mList ==
null
?
0
: mList.size();
}
/**
* 获取HeaderView的总数
*
* @return
*/
public
int
getHeaderViewCount() {
return
isHeaderViewEnable() ? mHeaderViews.size() :
0
;
}
/**
* 获取FooterView的总数
*
* @return
*/
public
int
getFooterViewCount() {
return
isFooterViewEnable() ? mFooterViews.size() :
0
;
}
/**
* 判断position位置是否为FooterView
*
* @param position
* @return
*/
public
boolean
isFooterView(
int
position) {
return
isFooterViewEnable() && isFooterViewPosition(position);
}
/**
* 判断position位置是否为HeaderView
*
* @param position
* @return
*/
public
boolean
isHeaderView(
int
position) {
return
isHeaderViewEnable() && isHeaderViewPosition(position);
}
/**
* 判断position位置是否为FooterView的索引
*
* @param position
* @return
*/
public
boolean
isFooterViewPosition(
int
position) {
return
position >= getItemDataCount() + getHeaderViewCount();
}
/**
* 判断position位置是否为HeaderView的索引
*
* @param position
* @return
*/
public
boolean
isHeaderViewPosition(
int
position) {
return
position < getHeaderViewCount();
}
/**
* 添加一个HeaderView
*
* @param headerView
*/
public
void
addHeaderView(View headerView) {
if
(headerView ==
null
) {
throw
new
NullPointerException(
"headerView is null"
);
}
mHeaderViews.put(TYPE_HEADER + getHeaderViewCount(), headerView);
notifyItemInserted(getHeaderViewCount() -
1
);
}
/**
* 添加一个FooterView
*
* @param footerView
*/
public
void
addFooterView(View footerView) {
if
(footerView ==
null
) {
throw
new
NullPointerException(
"footerView is null"
);
}
mFooterViews.put(TYPE_FOOTER + getFooterViewCount(), footerView);
notifyItemInserted(getHeaderViewCount() + getItemDataCount() + getFooterViewCount() -
1
);
}
三、RecyclerView使用注意
这里需要注明一点
RecyclerView
使用中的坑,如果RecyclerView
为LinearLayoutManager
时在onCreatViewHolder
中生成的View都必须关联上其parent
,也就是关联到RecyclerView
本身。我前面的一片文章记录了我遇到的这个问题RecyclerView
子View
宽度不充满父容器,所以在addHeaderView
和addFooterView
时也需要注意这个问题如果你的
RecyclerView
的LayoutManager
是GridLayoutManager
或StaggeredGridLayoutManager
时,如果就这样添加HeaderView
或FooterView
,会发现HeaderView
或FooterView
不会独立的占据一行。这是因为设置了SpanSize
的缘故,所以我们需要针对这两种LayoutManager
进行处理,处理方式如下:
代码:
@Override
public
void
onAttachedToRecyclerView(RecyclerView recyclerView) {
super
.onAttachedToRecyclerView(recyclerView);
final
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if
(layoutManager
instanceof
GridLayoutManager) {
((GridLayoutManager) layoutManager).setSpanSizeLookup(
new
GridLayoutManager.SpanSizeLookup() {
@Override
public
int
getSpanSize(
int
position) {
return
getNewSpanSize(((GridLayoutManager) layoutManager).getSpanCount(), position);
}
});
}
}
@Override
public
void
onViewAttachedToWindow(ViewHolder holder) {
super
.onViewAttachedToWindow(holder);
int
position = holder.getLayoutPosition();
if
(isHeaderView(position) || isFooterView(position)) {
final
ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
if
(layoutParams !=
null
&& layoutParams
instanceof
StaggeredGridLayoutManager.LayoutParams) {
StaggeredGridLayoutManager.LayoutParams lp = (StaggeredGridLayoutManager.LayoutParams) layoutParams;
lp.setFullSpan(
true
);
}
}
}
private
int
getNewSpanSize(
int
spanCount,
int
position) {
if
(isHeaderView(position) || isFooterView(position)) {
return
spanCount;
}
return
1
;
}
四、自动加载更多
自动加载更多也是列表显示中比较常见的一个功能,我们可以为RecyclerView
设置ScrollListener
监听来进行实现,具体实现的关键代码如下;
super
.setOnScrollListener(
new
OnScrollListener() {
@Override
public
void
onScrollStateChanged(RecyclerView recyclerView,
int
newState) {
super
.onScrollStateChanged(recyclerView, newState);
if
(newState == SCROLL_STATE_IDLE && mIsAutoLoadMore && mLoadMoreListener !=
null
) {
if
(mLastVisiblePosition +
1
== getAdapter().getItemCount()) {
mLoadMoreListener.onLoadMore();
}
}
if
(mOnScrollListener !=
null
) {
mOnScrollListener.onScrollStateChanged(recyclerView, newState);
}
}
@Override
public
void
onScrolled(RecyclerView recyclerView,
int
dx,
int
dy) {
super
.onScrolled(recyclerView, dx, dy);
if
(mIsAutoLoadMore && mLoadMoreListener !=
null
) {
mLastVisiblePosition = getLastVisiblePosition();
}
if
(mOnScrollListener !=
null
) {
mOnScrollListener.onScrolled(recyclerView, dx, dy);
}
}
});
相关代码和demo:
https://github.com/wangjing0311/AndroidDemo
RecyclerView更通用——listView的onItemClick,onLongItemClick,addHeaderView,addFooterView的更多相关文章
- RecyclerView的通用适配器,和滚动时不加载图片的封装
对于RecyclerView我们需要使用RecyclerAdapter,使用方式与ListViewAdapter类似,具体代码大家可以在网上搜索,这里就只教大家使用封装后的简洁RecyclerAdap ...
- RecyclerView的通用适配器
本来这一个主题应该早就写了,只是项目多,属于自己的时间不多,所以现在才开动!! 前一段时间写了一篇文章,是关于ListView,GriView万能适配器,没有看过的同学,可以先看看那篇文章,然后在来学 ...
- Android最新组件RecyclerView,替代ListView
转载请注明出处:http://blog.csdn.net/allen315410/article/details/40379159 万众瞩目的android最新5.0版本号不久前已经正式公布了,对于我 ...
- 为RecyclerView打造通用Adapter
##RecycleView简单介绍 RecyclerView控件和ListView的原理有非常多相似的地方,都是维护少量的View来进行显示大量的数据.只是RecyclerView控件比ListVie ...
- 浅谈RecyclerView(完美替代ListView,GridView)
Android RecyclerView 是Android5.0推出来的,导入support-v7包即可使用. 个人体验来说,RecyclerView绝对是一款功能强大的控件. 首先总结下Recycl ...
- RecyclerView高速通用适配Adapter
RecyclerView Adapter 为RecyclerView提供更简单的适配器实现方式,不断更新完好中. Demo视频演示 GitHub地址 博客 使用 BaseViewHolder 的使用 ...
- RecyclerView打造通用的万能Adapter
既然想做到通用那么现在摆在面前的就三个问题:数据怎么办?布局怎么办? 绑定怎么办?.数据决定采用泛型,布局打算直接构造传递,绑定显示效果肯定就只能回传. 1 基本改造 数据决定采用泛型,布局打算直接构 ...
- 为RecyclerView打造通用Adapter 让RecyclerView更加好用
原文出处: 张鸿洋 (Granker,@鸿洋_ ) 一.概述 记得好久以前针对ListView类控件写过一篇打造万能的ListView GridView 适配器,如今RecyclerView异军突起, ...
- RecyclerView(替代ListView)使用方法介绍
在build.gradle文件加入以下代码 compile 'com.android.support:cardview-v7:21.0.3' compile 'com.android.support: ...
随机推荐
- eval("("+json对象+")")
var obj=eval("("+data+")"); 看看下面这条,应该能想到json的数据结构“+(json对象名)+”由于json是以”{}”的方式来开始 ...
- UVA - 524 Prime Ring Problem(dfs回溯法)
UVA - 524 Prime Ring Problem Time Limit:3000MS Memory Limit:0KB 64bit IO Format:%lld & % ...
- mysql通过SOURCE导入SQL时报错处理
ERROR: unknown command '\n' Can't connect to the server 网上查询了,多少是编码问题引起,一边是UTF8一边是GBK,反复调整MY.CNF配置文件 ...
- LINUX下中文语言包的安装(转)
在安装盘上已经有各种语言包了,我们只需要找到他们,并安装就可以了.中文的是fonts-chinese-3.02-9.6.el5.noarch.rpmfonts-ISO8859-2-75dpi-1.0- ...
- 树形dp+MST-hdu-4126-Genghis Khan the Conqueror
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4126 题目意思: 给一图,n个点,m条边,每条边有个花费,给出q条可疑的边,每条边有新的花费,每条可 ...
- 14.4.5 System Tablespace 系统表空间
14.4.5 System Tablespace 系统表空间 InnoDB 系统表空间包含InnoDB 数据目录(元数据 用于InnoDB相关对象)和是存储区域用于doublewrite buffer ...
- 【Express】路由
var express = require('express'); var app = express(); app.set('port', process.env.PORT || 3000); ap ...
- 【转】Java基础笔记 – 枚举类型的使用介绍和静态导入--不错
原文网址:http://www.itzhai.com/java-based-notes-introduction-and-use-of-an-enumeration-type-static-impor ...
- HDOJ(HDU) 1785 You Are All Excellent(角度运算)
Problem Description 本次集训队共有30多人参加,毫无疑问,你们都是很优秀的,但是由于参赛名额有限,只能选拔部分队员参加省赛.从学校的角度,总是希望选拔出最优秀的18人组成6支队伍来 ...
- Java:Date、Calendar、Timestamp的区别、相互转换与使用【转载】
1 Java.util.Date 包含年.月.日.时.分.秒信息 包含年.月.日信息. 继承自java.util.Date.在数据库相关操作中使用,如rs.getDate,ps.setDate等.rs ...