自定义RecyclerView添加HeaderView,添加FooterView,实现滑动到底部,加载更多

显示效果图

同步更新CSDN

http://blog.csdn.net/wuyinlei/article/details/52662960

PS

接触过RecyclerView的应该会有个感觉,那就是我不想在使用ListView和GridView了,因为这个控件是可以实现那两个控件(ListView和GridView)所实现的几乎所有吧,哈哈我也没用他们俩干过多少的变种哈。所以在新项目中
自然也要使用这个RecyclerView来实现效果啊。

产品要求

头部可以任意定义的,比如说Banner图轮播,各种列表显示,几种分类,然后滑动到底部(RecyclerView)显示加载更多提示,然后子线程请求数据,进行数据加载,更新UI,如果没有数据,就给一个友好的用户提示。

刚开始想法

因为这个看起来很好办的,整个放到一个ScrollView里面,这样可以实现整体可以滑动,然后监听ScrollView滑动到底部的事件,然后去请求数据,但是我自己实现过一次,效果是可以了,但是滑动起来是有阻尼的(感觉啊),滑动不过2-3个item就会停止,感觉虽然效果实现了,但是用户体验却不怎么好,(PS:还没找到为啥有阻尼,可能自己使用了一个RelativeLayout的(实现上拉加载更多监听和下拉刷新监听的自定义类吧)),有空去自己看下原因吧。

去实现

说实话,对于RecyclerView自己还是了解一些的,也写过一些简单的介绍,之前也自己实现了一个下拉加载更多的提交到的了git,大家可以去参考下。(小弟功底有限,还请多多包涵https://github.com/wuyinlei/RecycleViewRefreshDemo),那么今天这个实现的上拉加载更多也是按照这个思路去实现的,就是通过监听RecyclerView滑动到底部的方法来判断是否要显示底布局,然后去加载数据。

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override 
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
            } 
 
            @Override 
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                //是否是最后一个显示的item位置
                int lastVisiableItemPosition = manager.findLastVisibleItemPosition();
                if (lastVisiableItemPosition + 1 == mCategoryAdapter.getItemCount()){
                    if (!isLoading){
                        isLoading = true;  //标志位  防止重复加载数据
                        handler.postDelayed(new Runnable() {
                            @Override 
                            public void run() { 
                                //requestData(); 
                                requestLoadMoreData();   //请求数据
                                //    Toast.makeText(MainActivity.this, "已经没有新的了", Toast.LENGTH_SHORT).show(); 
                                isLoading = false;  //加载完成数据   更新标志位
                                // adapter.notifyItemRemoved(adapter.getItemCount()); 
                            } 
                        },2000); 
                    } 
                } 
            } 
        }); 
 private void requestLoadMoreData(){ 
 
        index++;  //这个是模拟加载几次之后通知没有数据的
 
        if (index <= 3) {
            initData(); 
        } else { 
            Toast.makeText(MainActivity.this, "已经没有新的了", Toast.LENGTH_SHORT).show();
        } 
        // swipeRefreshLayout.setRefreshing(false); 
        mCategoryAdapter.notifyItemRemoved(mCategoryAdapter.getItemCount());  //加载完成之后移除footerView,也就是隐藏(去除加载中的view)
 
    } 
 

这样就可以很简单的实现上拉加载更多的逻辑实现了,(这个需要添加一个footerView,接下来就来分析一下如果添加FooterView,还有就是添加HeaderView)

Adapter如果写

首先定义三个变量用来说明是哪一个View,(HeaderView、View、FooterView)

    public static final int TYPE_HEADER = 0;
    public static final int TYPE_NORMAL = 1;
    private static final int TYPE_FOOTER = 2;
RecyclerView提供个getItemViewType(int position)方法来判断需要展示的哪种类型的View
        if (mHeaderView == null) return TYPE_NORMAL;
        if (position == 0) return TYPE_HEADER;
        if (mHeaderView != null && position +1 == getItemCount()) return TYPE_FOOTER;
        if (mHeaderView == null && position  == getItemCount()) return TYPE_FOOTER;
        return TYPE_NORMAL;

对于FooterView和HeaderView我们可以共用同一个ViewHolder(其实也就是使用一下,对于HeaderView的点击事件我们是在Activity或者Fragment里面去写的,后面有介绍)。这个时候我们在绑定ViewHolder的时候去判断我们需要展示的哪种View

  @Override 
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (mHeaderView != null && viewType == TYPE_HEADER) return new ViewHolder(mHeaderView);
        if (viewType == TYPE_FOOTER) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_foot, parent, false);
            return new FooterViewHolder(view);
        } 
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.category_item_layout, parent, false);
        return new ViewHolder(view);
    } 
对于正常View的ViewHolder也是正常的写,按照之前的正常使用RecyclerView的方式。例如:
class ViewHolder extends RecyclerView.ViewHolder { 
 
        private LinearLayout mCategoryLl;
        private ImageView mCategoryImg;
        private TextView mCategoryTitle, mCategoryDes;
 
        public ViewHolder(View itemView) {
            super(itemView);
            mCategoryImg = (ImageView) itemView.findViewById(R.id.category_book_img);
            mCategoryTitle = (TextView) itemView.findViewById(R.id.category_title);
            mCategoryDes = (TextView) itemView.findViewById(R.id.category_book_des);
            mCategoryLl = (LinearLayout) itemView.findViewById(R.id.category_ll);
        } 
    } 
对于HeaderView,我们的处理方式(在Adapter里面进行)
public void setHeaderView(View headerView) {
        mHeaderView = headerView;
        notifyItemInserted(0);   //位于顶部,通知一下view的第一项
    } 
这个时候通过在activity或者fragment里面进行
 private void setHeader(RecyclerView view) {
        //找到控件布局
        View header = LayoutInflater.from(this).inflate(R.layout.category_item_header, view, false);
 
        mRlBoy = (RelativeLayout) header.findViewById(R.id.rl_boy);
        mRlGirl = (RelativeLayout) header.findViewById(R.id.rl_girl);
        mRlEnd = (RelativeLayout) header.findViewById(R.id.rl_end);
        mRlUpdate = (RelativeLayout) header.findViewById(R.id.rl_update);
 
        mCategoryAdapter.setHeaderView(header); //设置headerview
    } 
HeaderView控件监听方式可以如下:
  private void initListener() { 
        mRlBoy.setOnClickListener(this);
        mRlGirl.setOnClickListener(this);
        mRlEnd.setOnClickListener(this);
        mRlUpdate.setOnClickListener(this);
    } 

基本到此为止,就能实现一般的需求了,如果有复杂的,应该还是存在其他方式的实现,以后有空或者有此需求之后自己在去研究。

解决在GridView形式下,HeaderView和FooterView只显示在最前面一个或者最后面一个(而不是占据一行bug)
 final GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
        gridLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(gridLayoutManager);

        // gridLayoutManager  布局管理器
        gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                //如果是第一个(添加HeaderView)   还有就是最后一个(FooterView)
                return position == mCategoryBean.size() + 1 || position == 0 ? gridLayoutManager.getSpanCount() : 1;
            }
        });

效果如开始哈,在次就贴下代码吧。

代码传动门:demo地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容