一、点击事件

setOnItemClickListener,setOnItemLongClickListener

RecyclerView中虽然没有提供上面这两个接口,但是给我们提供了另外一个接口:OnItemTouchListener看这个接口的文档描述我们知道此接口可以对RecyclerView中的手势进行监听处理,因此我们可以采用OnItemTouchListener+GestureDetector来实现RecyclerViewOnItemClickOnItemLongClick。实现方式也比较简单,还是上代码吧

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来进行区分展示,因此我这里的HeaderViewFooterView也是通过为它们设置不同的ItemViewType来进行区分展示。

我们知道ListView中的addHeaderViewaddFooterView都是可以添加多个View的,也就是说RecyclerView中也会出现添加多个完全不同的HeaderViewFooterView,所以我们必须为添加的每个HeaderViewFooterView都设置一个ItemViewType从而达到添加多个不同的HeaderViewFooterView的目的(如果所有的HeaderViewFooterView都设置同一个ItemViewType的话只能显示一种ViewHeaderViewFooterView)。

实现步骤

知道了实现原理,我们再来理一下实现步骤:

  1. 因为每个HeaderViewFooterView都需要对应一个ItemViewType,所以我们需要分别为它们建立一个映射关系,我采用SparseArray实现映射
  2. 我们需要在添加HeaderViewFooterView的时候生成对应的ItemViewType值,也就是我们需要定义一个ItemViewType的生成规则,我采用了基准值+视图个数的方式生成ItemViewType
  3. 自定义一个Adapter继承自RecyclerView.Adapter,重写里面的几个方法:onCreateViewHolder,onBindViewHolder,getItemViewType,getItemCount
  4. getItemCount方法中返回的数据总数显然是:HeaderView总数+FooterView总数+List列表展示的数据总数
  5. 重写onBindViewHoldergetItemViewType这两个方法时,显然需要根据position判断当前位置是否为HeaderView或是FooterView,而根据展示顺序来看当0<=position<HeaderView总数 时是HeaderView,而当position>=(HeaderView总数+List总数)时则是FooterView,其余位置则是List数据对应的View
  6. 而重写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使用注意

  1. 这里需要注明一点RecyclerView使用中的坑,如果RecyclerViewLinearLayoutManager时在onCreatViewHolder中生成的View都必须关联上其parent,也就是关联到RecyclerView本身。我前面的一片文章记录了我遇到的这个问题RecyclerViewView宽度不充满父容器,所以在addHeaderViewaddFooterView时也需要注意这个问题

  2. 如果你的RecyclerViewLayoutManagerGridLayoutManagerStaggeredGridLayoutManager时,如果就这样添加HeaderViewFooterView,会发现HeaderViewFooterView不会独立的占据一行。这是因为设置了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的更多相关文章

  1. RecyclerView的通用适配器,和滚动时不加载图片的封装

    对于RecyclerView我们需要使用RecyclerAdapter,使用方式与ListViewAdapter类似,具体代码大家可以在网上搜索,这里就只教大家使用封装后的简洁RecyclerAdap ...

  2. RecyclerView的通用适配器

    本来这一个主题应该早就写了,只是项目多,属于自己的时间不多,所以现在才开动!! 前一段时间写了一篇文章,是关于ListView,GriView万能适配器,没有看过的同学,可以先看看那篇文章,然后在来学 ...

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

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

  4. 为RecyclerView打造通用Adapter

    ##RecycleView简单介绍 RecyclerView控件和ListView的原理有非常多相似的地方,都是维护少量的View来进行显示大量的数据.只是RecyclerView控件比ListVie ...

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

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

  6. RecyclerView高速通用适配Adapter

    RecyclerView Adapter 为RecyclerView提供更简单的适配器实现方式,不断更新完好中. Demo视频演示 GitHub地址 博客 使用 BaseViewHolder 的使用 ...

  7. RecyclerView打造通用的万能Adapter

    既然想做到通用那么现在摆在面前的就三个问题:数据怎么办?布局怎么办? 绑定怎么办?.数据决定采用泛型,布局打算直接构造传递,绑定显示效果肯定就只能回传. 1 基本改造 数据决定采用泛型,布局打算直接构 ...

  8. 为RecyclerView打造通用Adapter 让RecyclerView更加好用

    原文出处: 张鸿洋 (Granker,@鸿洋_ ) 一.概述 记得好久以前针对ListView类控件写过一篇打造万能的ListView GridView 适配器,如今RecyclerView异军突起, ...

  9. RecyclerView(替代ListView)使用方法介绍

    在build.gradle文件加入以下代码 compile 'com.android.support:cardview-v7:21.0.3' compile 'com.android.support: ...

随机推荐

  1. MySql存储过程—3、变量

    1.变量的定义 在Mysql里面可以像我们写代码中一样定义变量来保持中间结果,看下面的格式: DECLARE variable_name datatype(size) DEFAULT default_ ...

  2. 在同个工程中使用 Swift 和 Objective-C(Swift 2.0更新)-b

    本节包含内容: Mix and Match 概述(Mix and Match Overview) 在同个应用的 target 中导入(Importing Code from Within the Sa ...

  3. Scheme是什么、怎么自定义Scheme、JLRoutes的使用-备

    转到移动端开发后居然现在才用到Scheme真是惭愧惭愧. URL Scheme是什么 相信大家都知道URL. http://www.apple.com就是一个URL. 而://之前的部分就称为Sche ...

  4. MySQL重置root密码的几种方法(windows+Linux)

    重置root密码的方法: windows系统下:1.停止mysql服务:2.新建文件init-root.txt,写上如下内容: update mysql.user set password = pas ...

  5. DC DC電路電感的選擇

    注:只有充分理解電感在DC/DC電路中發揮的作用,才能更優的設計DC/DC電路.本文還包括對同步DC/DC及異步DC/DC概念的解釋.   DCDC電路電感的選擇 簡介 在開關電源的設計中電感的設計為 ...

  6. java实现DES加密与解密,md5加密

    很多时候要对秘要进行持久化加密,此时的加密采用md5.采用对称加密的时候就采用DES方法了 import java.io.IOException; import java.security.Messa ...

  7. Facebook 开源 AI 所使用的硬件平台 'Big Sur'

    Facebook 开源 AI 所使用的硬件平台 'Big Sur' Facebook 今开源其 AI 所使用的硬件平台 'Big Sur'.'Big Sur' 是兼容开放机架的 GPU 加速硬件平台. ...

  8. POJ2513 Colored Sticks(欧拉)

    题目链接. 题目大意: 给很多木棍,两端被涂了颜色.任意两根木棍的相同颜色处可以拼接在一起,问有没有可能将所有的木棍都连起来,成一条直线? 分析: 考点,欧拉道路. 将一根木棍看成一条边,两端的颜色看 ...

  9. BZOJ2274: [Usaco2011 Feb]Generic Cow Protests

    2274: [Usaco2011 Feb]Generic Cow Protests Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 196  Solve ...

  10. 【数学】HDU 5761 Rower Bo

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5761 题目大意: 船在(0,a),船速v1,水速v2沿x轴正向,船头始终指向(0,0),问到达(0, ...