RecyclerView添加头部和底部视图的实现

简介: ListView是有addHeaderView和 addFooterView两个方法的. 但是作为官方推荐的ListView的升级版RecyclerView缺无法实现这两个方法。 那么如果使用RecyclerView实现这两个方法的效果该怎么做呢? 网上查询了很久,试过各种各样的实现方式,终于让我发现一个还不错的实现方法,那么就给大家推荐一下。

ListView是有addHeaderView和 addFooterView两个方法的.

但是作为官方推荐的ListView的升级版RecyclerView缺无法实现这两个方法。

那么如果使用RecyclerView实现这两个方法的效果该怎么做呢?

网上查询了很久,试过各种各样的实现方式,终于让我发现一个还不错的实现方法,那么就给大家推荐一下。

项目地址(别人写的,非博主的)https://github.com/jczmdeveloper/XCRecyclerView

我看了下这个源码,很简单,即写了一个继承RecyclerView的控件,自己实现addHeaderView和addFooterView两个方法

package com.xqx.com.recyclerviewheaderdemo;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;


public class XCRecyclerView extends RecyclerView{

    private ArrayList<View> mHeaderViews = new ArrayList<>();
    private ArrayList<View> mFooterViews = new ArrayList<>();
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.Adapter mWrapAdapter;
    private static final int TYPE_HEADER = -101;
    private static final int TYPE_FOOTER  = -102;
    private static final int TYPE_LIST_ITEM = - 103;
    public XCRecyclerView(Context context) {
        this(context, null);
    }
    public XCRecyclerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public XCRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }
    private void init(Context context){

    }

    @Override
    public void setAdapter(Adapter adapter) {
        mAdapter = adapter;
        mWrapAdapter = new WrapAdapter(mHeaderViews, mFooterViews, adapter);
        super.setAdapter(mWrapAdapter);
        mAdapter.registerAdapterDataObserver(mDataObserver);
    }
    public void addHeaderView(View view){
        mHeaderViews.clear();
        mHeaderViews.add(view);
    }
    public void addFooterView(View view){
        mFooterViews.clear();
        mFooterViews.add(view);
    }
    public int getHeaderViewsCount(){
        return mHeaderViews.size();
    }
    public int getFooterViewsCount(){
        return mFooterViews.size();
    }
    private final RecyclerView.AdapterDataObserver mDataObserver = new RecyclerView.AdapterDataObserver() {
        @Override
        public void onChanged() {
            mWrapAdapter.notifyDataSetChanged();
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount) {
            mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount);
        }

//        @Override
//        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
//            mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount, payload);
//        }

        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            mWrapAdapter.notifyItemRangeInserted(positionStart, itemCount);
        }

        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            mWrapAdapter.notifyItemMoved(fromPosition, toPosition);
        }

        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            mWrapAdapter.notifyItemRangeRemoved(positionStart, itemCount);
        }
    };
    private class WrapAdapter extends RecyclerView.Adapter<ViewHolder>{

        private Adapter mAdapter;
        private List<View> mHeaderViews;
        private List<View> mFooterViews;
        public WrapAdapter(List<View> headerViews,List<View> footerViews,Adapter adapter){
            this.mAdapter = adapter;
            this.mHeaderViews = headerViews;
            this.mFooterViews = footerViews;
        }

        public int getHeaderCount(){
            return this.mHeaderViews.size();
        }
        public int getFooterCount(){
            return this.mFooterViews.size();
        }
        public boolean isHeader(int position){
            return position >= 0 && position < this.mHeaderViews.size();
        }
        public boolean isFooter(int position){
            return position < getItemCount() && position >= getItemCount() - this.mFooterViews.size();
        }
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            if(viewType == TYPE_HEADER){
                return new CustomViewHolder(this.mHeaderViews.get(0));
            }else if(viewType == TYPE_FOOTER){
                return new CustomViewHolder(this.mFooterViews.get(0));
            }else{
                return this.mAdapter.onCreateViewHolder(parent,viewType);
            }
        }

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            if(isHeader(position)) return;
            if(isFooter(position)) return;
            int rePosition = position - getHeaderCount();
            int itemCount = this.mAdapter.getItemCount();
            if(this.mAdapter != null){
                if(rePosition < itemCount){
                    Log.v("czm","rePosition/itemCount="+rePosition+"/"+itemCount);
                    this.mAdapter.onBindViewHolder(holder,rePosition);
                    return;
                }
            }
        }
        @Override
        public long getItemId(int position) {
            if (this.mAdapter != null && position >= getHeaderCount()) {
                int rePosition = position - getHeaderCount();
                int itemCount = this.mAdapter.getItemCount();
                if (rePosition < itemCount) {
                    return this.mAdapter.getItemId(rePosition);
                }
            }
            return -1;
        }
        @Override
        public int getItemViewType(int position) {
            if(isHeader(position)){
                return TYPE_HEADER;
            }
            if(isFooter(position)){
                return TYPE_FOOTER;
            }
            int rePosition = position - getHeaderCount();
            int itemCount = this.mAdapter.getItemCount();
            if(rePosition < itemCount){
                return this.mAdapter.getItemViewType(position);
            }
            return TYPE_LIST_ITEM;
        }
        @Override
        public int getItemCount() {
            if(this.mAdapter != null){
                return getHeaderCount() + getFooterCount() + this.mAdapter.getItemCount();
            }else{
                return getHeaderCount() + getFooterCount();
            }
        }

        @Override
        public void registerAdapterDataObserver(AdapterDataObserver observer) {
            if(this.mAdapter != null){
                this.mAdapter.registerAdapterDataObserver(observer);
            }
        }

        @Override
        public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
            if(this.mAdapter != null){
                this.mAdapter.unregisterAdapterDataObserver(observer);
            }
        }

        private class CustomViewHolder extends ViewHolder{

            public CustomViewHolder(View itemView) {
                super(itemView);
            }
        }
    }
}
XCRecyclerView

使用方法github里也写的清清楚楚的

private MyAdapter mAdapter;
private XCRecyclerView mRecyclerView;
private List<String> mData;
private View mHeaderView;
private View mFooterView;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mData = new  ArrayList<String>();
    for(int i = 0; i < 10 ;i++){
        mData.add("item_" + i);
    }
    mAdapter = new MyAdapter(mData);
    mRecyclerView = (XCRecyclerView) findViewById(R.id.recycler_view);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    mHeaderView = LayoutInflater.from(this).inflate(R.layout.layout_header,mRecyclerView,false);
    mFooterView = LayoutInflater.from(this).inflate(R.layout.layout_footer,mRecyclerView,false);
    mRecyclerView.addHeaderView(mHeaderView);
    mRecyclerView.addFooterView(mFooterView);
    mRecyclerView.setAdapter(mAdapter);
}

注意点:

addHeaderView之后 列表的数据坐标即相应发生变化!即addHeadView一次,列表第一个数据的下坐标+1(0-->1)

adapter.notifyItemChanged();等方法的坐标类似,都要相应的变化。

比如你addHeadView()一次

那么你想更新列表第4个列表项的视图,则adapter.notifyItemChanged(3+1);  多加1  headView也算一个列表项。 

相关文章
|
XML 前端开发 Android开发
Android NestedScrollView滚动到顶部固定子View悬停挂靠粘在顶端
Android NestedScrollView滚动到顶部固定子View悬停挂靠粘在顶端 网上有一个StickyScrollView,称之为粘性ScrollView,比如一个垂直方向的布局,依次摆放几个子View,当某一个子View滚到到顶端时候要停靠在顶部,悬停在顶部的位置不动。
2788 0
渐变导航--滑动固定头部
渐变导航--滑动固定头部
|
10月前
|
XML Android开发 数据格式
控件布局(View)叠加效果
控件布局(View)叠加效果
|
计算机视觉
RecyclerView#Adapter支持无数据布局、错误布局和列表尾部的”没有更多了“布局
实际开发中,UI小姐姐都会提供通用的`无数据页面`、`错误提示页面`。 针对常见的`支持下拉刷新和上拉加载更多的列表页面`,将他们的通用逻辑抽取出来,这样我们在开发过程中就只需要关注具体的业务逻辑了,无需每次通过cv来完善`无数据页面`、`错误提示页面`的逻辑了。
|
前端开发 C#
【C#/WPF】ListView的MVVM例子,及禁止拖动ListView的头部Header
原文:【C#/WPF】ListView的MVVM例子,及禁止拖动ListView的头部Header 一个ListView的MVVM简单例子: ...
1543 0
|
Android开发
让 Toolbar 随着 RecyclerView 的滚动而显示/隐藏
本文讲的是让 Toolbar 随着 RecyclerView 的滚动而显示/隐藏,在这篇文章中,我们将看到如何实现像Google+ 应用程序一样,当列表下滑时,Toolbar和FAB(包括其他的View)隐藏;当列表上滑时,Toolbar和FAB(包括其他的View)显示的效果;这种效果在Material Design Checklist提到过.
1837 0
|
前端开发 Android开发
解决Scrollview 嵌套recyclerview不能显示,高度不正常的问题
我们先看一个效果,问题说的就是中间的Grid效果在Scrollview 嵌套recyclerview显示问题,在Android Api 24是好的,不过在5,1,1版本(api 22)缺出现了问题 最近项目中,有一个商品详情页面,页面有好几个网格页面,大家说,我们大可以用GridView去做,但是需要方的要求是,我们的网格的中间的线怎么做呢,对于GridView,我们知道我们可以这是一个背景
3735 0
listview滚动到底部
方法一: // msgListView是ListView控件 // adapter是ListView绑定的Adapter,如果不方便直接使用,也可以通过ListView的getAdapter()方法获取到,前提是你已经绑定了适配器哦 // 里面的参数就很熟悉了吧,其实这个方法的主要...
808 0