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的ItemViewTypeprivate SparseArray<view> mHeaderViews = new SparseArray<>(0);//存储FooterView,key值作为对应HeaderView的ItemViewTypeprivate SparseArray<view> mFooterViews = new SparseArray<>(0); @Overridepublic 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);} @Overridepublic final void onBindViewHolder(ViewHolder holder, int position) { if(isFooterView(position) || isHeaderView(position)) { return; } T item = getItem(position - getHeaderViewCount()); onBindItemViewHolder(holder, position, item);} @Overridepublic 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*/@Overridepublic 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进行处理,处理方式如下:
代码:
@Overridepublic 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); } }); }} @Overridepublic 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: ...
随机推荐
- Js屏蔽键盘事件
<script> function KeyDown(){ //屏蔽鼠标右键.Ctrl+n.shift+F10.F5刷新.退格键 //alert(& ...
- Python环境配置及项目建立
一.安装Python Python比较稳定的两个版本是Python 3.5和Python 2.7,我用的是Python 2.7,下载地址是:https://www.python.org/downloa ...
- UVA - 11346 Probability (概率)
Description Probability Time Limit: 1 sec Memory Limit: 16MB Consider rectangular coordinate system ...
- 转:玩转Google开源C++单元测试框架Google Test系列
转自http://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html 前段时间学习和了解了下Google的开源C++单元测试框架Google ...
- SQLSERVER一个比较不错的分页存储过程p_splitpage
CREATE procedure p_splitpage @sql nvarchar(4000), --要执行的sql语句 @page int=1, --要显示的页码 @pageSize int, - ...
- c语言之函数指针
一.基础研究 这里研究的内容是函数指针,需要我们在研究后构造程序来描述函数指针数组的用法和向函数传函数指针的方法. 指针有很多种:整型指针.结构体指针.数组指针等等,它们的本质是它们的值都是一个地址, ...
- [转]C# FileSystemWatcher监控指定文件或目录的文件的创建、删除、改动、重命名等活动
觉得这个很常用..比如一些软件. http://www.rabbit8.cn/DoNet/407.html FileSystemWatcher控件主要功能: 监控指定文件或目录的文件的创建.删 ...
- mapreduce (二) MapReduce实现倒排索引(一) combiner是把同一个机器上的多个map的结果先聚合一次
1 思路:0.txt MapReduce is simple1.txt MapReduce is powerfull is simple2.txt Hello MapReduce bye MapRed ...
- SCALA中的抽象类代码样例
package com.hengheng.scala class AbstractClass { } abstract class People { def speak val name : Stri ...
- 利用readwritelock简单模拟实现多线程下cache的系统
package cn.lyy.hibernate.many2one; import java.util.HashMap; import java.util.Map; import java.util. ...