封装RecyclerView,实现添加头部和底部

我们都知道ListView具有添加头部和添加底部的方法,但是RecyclerView并没有这样子的方法。所以RecyclerView是不能添加底部和头部的,但是能不能仿造ListView来实现RecyclerView添加头部和底部呢?答案当然是可行的。 首先看下实现的效果:

public class WrapMyRecyclerView extends RecyclerView {

    private Adapter mAdapter;
    private ArrayList<View> mHeaderViewInfos = new ArrayList<>();//保存headerView
    private ArrayList<View> mFooterViewInfos = new ArrayList<>();//保存footerView

    public WrapMyRecyclerView(Context context) {
        super(context);
    }

    public WrapMyRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public WrapMyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /**
     * 添加headerView
     * @param v
     */
    public void addHeaderView(View v) {
        mHeaderViewInfos.add(v);
        // Wrap the adapter if it wasn't already wrapped.
        if (mAdapter != null) {
            if (!(mAdapter instanceof RecyclerHeaderViewListAdapter)) {
                wrapHeaderListAdapterInternal();
            }
        }
    }

    /**
     * 添加一个footerView
     * @param v
     */
    public void addFooterView(View v) {
        mFooterViewInfos.add(v);
        // Wrap the adapter if it wasn't already wrapped.
        if (mAdapter != null) {
            if (!(mAdapter instanceof RecyclerHeaderViewListAdapter)) {
                wrapHeaderListAdapterInternal();
            }
        }
    }

    /**
     * 设置一个Adapter
     * @param adapter
     */
    @Override
    public void setAdapter(Adapter adapter) {

        if (mHeaderViewInfos.size() > 0 || mFooterViewInfos.size() > 0) {
            mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
        } else {
            mAdapter = adapter;
        }
        super.setAdapter(mAdapter);
    }

    private void wrapHeaderListAdapterInternal() {
        mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);
    }

    /**
     * 新建一个RecyclerHeaderViewListAdapter对象
     * 最终的adapter实现它
     * @param headerViewInfos
     * @param footerViewInfos
     * @param adapter
     * @return
     */
    protected RecyclerHeaderViewListAdapter wrapHeaderListAdapterInternal(
            ArrayList<View> headerViewInfos,
            ArrayList<View> footerViewInfos,
            Adapter adapter) {
        return new RecyclerHeaderViewListAdapter(headerViewInfos, footerViewInfos, adapter);
    }

}

复制代码

这就是封装的RecyclerView,里面主要有三个方法addHeaderView、 addFooterView和重写的setAdapter。这里的唯一的思想就是偷梁换柱,当我们添加头部、尾部或者设置adapter时,真正的adapter并不是我们传入的adapter,而是重新new 了一个RecyclerHeaderViewListAdapter。这才是RecyclerView最终设置的adapter。其实我们看到ListView也是通过这样子的思想来添加头部和尾部的。

这就是ListView的addHeaderView方法,它会偷偷的创建HeaderViewListAdapter这个adapter。最终添加的header和footer在HeaderViewListAdapter里面实现。

public class RecyclerHeaderViewListAdapter extends RecyclerView.Adapter {

    private final ArrayList<View> mHeaderViewInfos;//保存headerview数据
    private final ArrayList<View> mFooterViewInfos;//保存footerView数据
    private RecyclerView.Adapter mAdapter;         //用户自己构造的adapter
    private static final int RECYCLER_HEADER_VIEW = 0x001;//headerview类型
    private static final int RECYCLER_FOOTER_VIEW = 0x002;//footerView类型


    /**
     * 构造方法
     * 初始化
     * @param headerViewInfos
     * @param footerViewInfos
     * @param adapter
     */
    public RecyclerHeaderViewListAdapter(ArrayList<View> headerViewInfos,
                                         ArrayList<View> footerViewInfos,
                                         RecyclerView.Adapter adapter) {

        mAdapter = adapter;
        if (headerViewInfos == null) {
            mHeaderViewInfos = new ArrayList<>();
        } else {
            mHeaderViewInfos = headerViewInfos;
        }

        if (footerViewInfos == null) {
            mFooterViewInfos = new ArrayList<>();
        } else {
            mFooterViewInfos = footerViewInfos;
        }
    }

    /**
     * 根据getItemViewType返回的条目类型
     * 创建不同的itemview
     * 传入的adapter,回调它的onCreateViewHolder即可
     * @param parent
     * @param viewType
     * @return
     */
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == RECYCLER_HEADER_VIEW){
            return new HeaderViewLayout(mHeaderViewInfos.get(0));
        }else if (viewType == RECYCLER_FOOTER_VIEW){
            return new HeaderViewLayout(mFooterViewInfos.get(0));
        }
        return mAdapter.onCreateViewHolder(parent,viewType);
    }

    /**
     * 绑定数据
     * headerview和footerview不需要绑定数据,直接return即可
     * 传入的adapter需要回调它的onBindViewHolder即可
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        //header
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {
            return ;
        }
        //adapter body
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getItemCount();
            if (adjPosition < adapterCount) {
                mAdapter.onBindViewHolder(holder, adjPosition);
                return ;
            }
        }
        //footer
    }

    /**
     * 返回条目的类型
     * 传入的adapter,回调它的getItemViewType即可
     * @param position
     * @return
     */
    @Override
    public int getItemViewType(int position) {
        // Header
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {
            return RECYCLER_HEADER_VIEW;
        }
        // Adapter
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getItemCount();
            if (adjPosition < adapterCount) {
                return mAdapter.getItemViewType(position);
            }
        }
        // Footer (off-limits positions will throw an IndexOutOfBoundsException)
        return RECYCLER_FOOTER_VIEW;
    }

    /**
     * 总条目即:footerview的条目+headerview的条目+穿入的adapter条目
     * @return
     */
    @Override
    public int getItemCount() {
        if (mAdapter != null) {
            return getFootersCount() + getHeadersCount() + mAdapter.getItemCount();
        } else {
            return getFootersCount() + getHeadersCount();
        }
    }

    /**
     * 获取headerview的条目
     * @return
     */
    public int getHeadersCount() {
        return mHeaderViewInfos.size();
    }

    /**
     * 获取footervie的条目
     * @return
     */
    public int getFootersCount() {
        return mFooterViewInfos.size();
    }

    /**
     * 这是footerview和headerview的ViewHolder需要
     * 这里只是提供一个构造器即可,实际上用处不大
     */
    private static class HeaderViewLayout extends RecyclerView.ViewHolder{

        public HeaderViewLayout(View itemView) {
            super(itemView);
        }
    }

}

复制代码

这是实现添加header、footer和传入adapter的RecyclerHeaderViewListAdapter。具体的逻辑都在文件的注释里面有说明。逻辑是仿造ListView的HeaderViewListAdapter来实现的。 其实就是创建一个adapter,然后根据不同的条目类型来创建条目和绑定条目的数据即可。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.wrap.recycler.WrapRecyclerViewActivity">

    <com.lwj.wrap.recycler.WrapMyRecyclerView
        android:id="@+id/wrap_recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

复制代码

这是布局文件

public class WrapRecyclerViewActivity extends AppCompatActivity {
    private WrapMyRecyclerView mRecyclerView;
    private MyAdapter mMyAdapter;
    private List<String> mList01 = new ArrayList<>();
    private static final int WC = ViewGroup.LayoutParams.WRAP_CONTENT;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wrap_recycler_view);
        getData();
        mRecyclerView = (WrapMyRecyclerView) this.findViewById(R.id.wrap_recyclerview);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

        //加入headerView
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(WC,WC);
        ImageView headerView = new ImageView(this);
        headerView.setImageResource(R.drawable.timg);
        headerView.setLayoutParams(params);
        mRecyclerView.addHeaderView(headerView);
        //设置adapter
        mMyAdapter = new MyAdapter(this,mList01);
        mRecyclerView.setAdapter(mMyAdapter);
        //加入footerView
        params = new ViewGroup.LayoutParams(WC,WC);
        ImageView footerView = new ImageView(this);
        footerView.setImageResource(R.drawable.hhfj);
        footerView.setLayoutParams(params);
        mRecyclerView.addFooterView(footerView);
        mMyAdapter.notifyDataSetChanged();

    }
    public void getData() {
        for (int i = 0; i < 3; i++) {
            String data ="adapter...." + i;
            mList01.add(data);
        }
    }
}

复制代码

使用直接调用addHeaderView、addFooterView即可。

除了这种方式来实现addHeaderView和addFooterView,另外一种方式就是封装Adapter来实现,原理还是保持不变:根据不同的条目类型来创建条目和绑定条目的数据。

public class MyCircleRecycViewAdapter extends RecyclerView.Adapter {
    public List<CircleInfo.CirclePageInfo> infos = null;
    private Context mContext;
    private ListViewImgLoader mLoader;
    private View VIEW_FOOTER;//尾部
    private View VIEW_HEADER;//头部
    //Type
    private int TYPE_NORMAL = 1000;
    private int TYPE_HEADER = 1001;
    private int TYPE_FOOTER = 1002;
    private int tagType = TYPE_NORMAL;

    public MyCircleRecycViewAdapter(Context context,List<CircleInfo.CirclePageInfo> datas) {
        this.infos = datas;
        this.mContext = context;
        mLoader = new ListViewImgLoader();
        mLoader.setMemoryCacheSize(1024 * 1024);
        mLoader.setVisibleItemCount(12);
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == TYPE_FOOTER) {
            tagType = TYPE_FOOTER;
            return new MyCircleItemHolder(VIEW_FOOTER);
        } else if (viewType == TYPE_HEADER) {
            tagType = TYPE_HEADER;
            return new MyCircleItemHolder(VIEW_HEADER);
        } else {
            tagType = TYPE_NORMAL;
            View view = LayoutInflater.from(mContext).inflate(R.layout.circle_gridview_items, parent,false);
            return new MyCircleItemHolder(view);
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (!isHeaderView(position) && !isFooterView(position)) {
            if (haveHeaderView()) position--;
            MyCircleItemHolder viewHolder = (MyCircleItemHolder)holder;
            CircleInfo.CirclePageInfo mInfo = infos.get(position);
            setData(viewHolder,mInfo);
        }
    }

    @Override
    public int getItemCount() {
        int count = (infos == null ? 0 : infos.size());
        if (VIEW_FOOTER != null) {
            count++;
        }
        if (VIEW_HEADER != null) {
            count++;
        }
        return count;
    }

    @Override
    public int getItemViewType(int position)
    {
        if (isHeaderView(position)) {
            return TYPE_HEADER;
        } else if (isFooterView(position)) {
            return TYPE_FOOTER;
        } else {
            return TYPE_NORMAL;
        }
    }
    public void addHeaderView(View headerView) {
        if (haveHeaderView()) {
            throw new IllegalStateException("hearview has already exists!");
        } else {
            //避免出现宽度自适应
            ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(Utils.getRealPixel(30), Utils.getRealPixel(230));
            headerView.setLayoutParams(params);
            VIEW_HEADER = headerView;
            notifyItemInserted(0);
        }
    }
    public void addFooterView(View footerView) {
        if (haveFooterView()) {
            throw new IllegalStateException("footerView has already exists!");
        } else {
            ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            footerView.setLayoutParams(params);
            VIEW_FOOTER = footerView;
//            ifGridLayoutManager();
            notifyItemInserted(getItemCount() - 1);
        }
    }

    private boolean haveHeaderView() {
        return VIEW_HEADER != null;
    }

    public boolean haveFooterView() {
        return VIEW_FOOTER != null;
    }

    private boolean isHeaderView(int position) {
        return haveHeaderView() && position == 0;
    }

    private boolean isFooterView(int position) {
        return haveFooterView() && position == getItemCount() - 1;
    }


    private void setData(final MyCircleItemHolder viewHolder, final CircleInfo.CirclePageInfo mInfo) {
        if(mInfo == null || mInfo == viewHolder.tagInfo)
        {
            return;
        }else{
            viewHolder.tagInfo = mInfo;
            if(!StrUtils.isEnpty(mInfo.thread_unread)){
                if(mInfo.thread_unread.equals("0")){
                    viewHolder.threaduUnreadTv.setVisibility(View.INVISIBLE);
                }else{
                    viewHolder.threaduUnreadTv.setVisibility(View.VISIBLE);
                    viewHolder.threaduUnreadTv.setText(mInfo.thread_unread);
                }
            }else{
                viewHolder.threaduUnreadTv.setVisibility(View.INVISIBLE);
            }

            if (!TextUtils.isEmpty(viewHolder.tagUrl)){
                if(mInfo.circle_img_path != null && !(mInfo.circle_img_path.equals(viewHolder.tagUrl))){
                    setImage(viewHolder.mImageView,mInfo.circle_img_path);
                }
            }else{
                setImage(viewHolder.mImageView,mInfo.circle_img_path);
            }
            if (!TextUtils.isEmpty(mInfo.circle_img_path)){
                viewHolder.tagUrl = mInfo.circle_img_path;
            }
            if(mInfo.circleName != null){
                if(mInfo.circleName.length() > 5){
                    String s = mInfo.circleName.substring(0,5) + "...";
                    viewHolder.textView.setText(s);
                }else{
                    viewHolder.textView.setText(mInfo.circleName);
                }
            }

        }
    }

    private void setImage(final ColorFilterImageView mImageView, final String imgUrl){
        mImageView.setBackgroundColor(0xffadadad);
        mImageView.setImageBitmap(null);
        if(!TextUtils.isEmpty(imgUrl))
        {
            mLoader.loadImage(mImageView.hashCode(), imgUrl, 300, new DnImg.OnDnImgListener()
            {

                @Override
                public void onProgress(String url, int downloadedSize, int totalSize)
                {
                    // TODO Auto-generated method stub

                }

                @Override
                public void onFinish(String url, String file, Bitmap bmp)
                {
                    if(url.equals(imgUrl))
                    {
                        mImageView.setImageBitmap(bmp);
                    }
                }
            });
        }else{
            mImageView.setBackgroundColor(0xffadadad);
            mImageView.setImageBitmap(null);
        }
    }


    class MyCircleItemHolder extends RecyclerView.ViewHolder{
        private ColorFilterImageView mImageView;
        private TextView textView;
        private CircleInfo.CirclePageInfo tagInfo;
        private String tagUrl;
        private TextView threaduUnreadTv;

        public MyCircleItemHolder(final View itemView) {
            super(itemView);
            if(tagType ==  TYPE_NORMAL){
                mImageView = (ColorFilterImageView)itemView.findViewById(R.id.quan_icon);
                mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                textView = (TextView)itemView.findViewById(R.id.quan_name);
                threaduUnreadTv = (TextView)itemView.findViewById(R.id.quan_num);
                mImageView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (mListener != null){
                            mListener.oncliItem(itemView,tagInfo,threaduUnreadTv);
                        }
                    }
                });
            }

        }
    }
    public OnclickMyCircleItemListener mListener;
    public void setOnclickMyCircleItemListener(OnclickMyCircleItemListener l){
        this.mListener = l;
    }
    public interface OnclickMyCircleItemListener{
        void oncliItem(View view,CircleInfo.CirclePageInfo info,View threadNumTv);
    }

    public void pauseLoader(){
        if(mLoader != null)
        {
            mLoader.pause();
        }
    }
    public void resumeLoader(){
        if(mLoader != null)
        {
            mLoader.resume();
        }
    }
    public void closeLoader(){
        if(mLoader != null)
        {
            mLoader.close();
        }
    }

}

复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值