RecyclerView更通用——listView的onItemClick,onLongItemClick,addHeaderView,addFooterView

时间:2023-02-25 09:33:19

一、点击事件

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&lpar;完美替代ListView&comma;GridView&rpar;

    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. Access使用join进行多个表联合查询的问题

    Access是支持三表或三表以上的join查询的,但是要加括号,如果不加的话,会报错,括号的作用是决定join的顺序.例如: SELECT *FROM (aa LEFT JOIN bb ON aa.a ...

  2. html,移动端代码

    <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale= ...

  3. 给Xcode配置VVDocumenter-Xcode-master,注释插件

    1.      去github上下载     https://github.com/onevcat/VVDocumenter-Xcode   . 2.      打开工程,command+B 编译成功 ...

  4. O&lpar;1&rpar;检测2的幂次

    class Solution { public: /* * @param n: An integer * @return: True or false */ bool checkPowerOf2(in ...

  5. HDOJ&sol;HDU 2547 无剑无我&lpar;两点间的距离&rpar;

    Problem Description 北宋末年,奸臣当道,宦官*,外侮日亟,辽军再犯.时下战火连连,烽烟四起,哀鸿遍野,民不聊生,又有众多能人异士群起而反,天下志士云集响应,景粮影从. 值此危急存 ...

  6. Java学习日记-6 继承

    继承1.基本介绍 面向对象程序设计三大原则之一.被继承的称为父类,继承类称为子类.关键字:extends例子: class TwoDshape{ double width; double height ...

  7. Android 去掉Activity的跳转动画

    startActivity或finish的时候调用一句话即可: overridePendingTransition(0, 0);

  8. linux下pppoe连接管理

    一.安装pppoe组件 sudo apt-get install pppoe pppoeconf 二.配置pppoe 图形界面配置pppoe,在terminal里输入 nm-connection-ed ...

  9. &lbrack;转&rsqb;有return的情况下try catch finally的执行顺序

    结论: 1.不管有没有出现异常,finally块中代码都会执行: 2.当try和catch中有return时,finally仍然会执行: 3.finally是在return后面的表达式运算后执行的(此 ...

  10. day31-软件开发规范

    一.为什么要规范软件开发? 1.1 为什么要有规范软件开发 你现在包括之前写的一些程序,所谓的'项目',都是在一个py文件下完成的,代码量撑死也就几百行,你认为没问题,挺好.但是真正的后端开发的项目, ...