标签:
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);
}
前面写过一篇文章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
为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);
}
}
});
https://github.com/wangjing0311/AndroidDemo
RecyclerView更通用——listView的onItemClick,onLongItemClick,addHeaderView,addFooterView
标签:
原文地址:http://www.cnblogs.com/wangjinger/p/RecycleView.html