Android 解析RecyclerView(2)——带顶部View和底部View的RecyclerView

RecyclerView是用来替代ListView的一个控件,比ListView更加的简洁高效,不过也有一些比较不足的地方,比如:无法直接设置点击事件监听; 无法像ListView那样直接添加顶部View和底部View。

设置点击事件监听在前一篇文章已经解决了,这一篇文章要来介绍如何为RecyclerView添加顶部View和底部View。

一、源码分析

先来看下ListView的源码,研究它是如何添加顶部View的

public void addHeaderView(View v, Object data, boolean isSelectable) {
        final FixedViewInfo info = new FixedViewInfo();
        info.view = v;
        info.data = data;
        info.isSelectable = isSelectable;
        mHeaderViewInfos.add(info);
        mAreAllItemsSelectable &= isSelectable;

        // Wrap the adapter if it wasn't already wrapped.
        if (mAdapter != null) {
            if (!(mAdapter instanceof HeaderViewListAdapter)) {
                wrapHeaderListAdapterInternal();
            }

            // In the case of re-adding a header view, or adding one later on,
            // we need to notify the observer.
            if (mDataSetObserver != null) {
                mDataSetObserver.onChanged();
            }
        }
    }

可以看到,addHeaderView() 方法首先是将 headerView 保存到 FixedViewInfo 对象中,再将 FixedViewInfo 对象保存到集合中。 FixedViewInfo 类的定义如下所示:

   public class FixedViewInfo {
        /** The view to add to the list */
        public View view;
        /** The data backing the view. This is returned from {@link ListAdapter#getItem(int)}. */
        public Object data;
        /** <code>true</code> if the fixed view should be selectable in the list */
        public boolean isSelectable;
    }

mAdapter 即是为ListView设置的Adapter 对象,如果 mAdapter 不是HeaderViewListAdapter 的直接实例,则调用 wrapHeaderListAdapterInternal() 方法,以顶部View、底部View和mAdapter为参数,来将 mAdapter 转为 HeaderViewListAdapter 对象

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

    protected HeaderViewListAdapter wrapHeaderListAdapterInternal(
            ArrayList<ListView.FixedViewInfo> headerViewInfos,
            ArrayList<ListView.FixedViewInfo> footerViewInfos,
            ListAdapter adapter) {
        return new HeaderViewListAdapter(headerViewInfos, footerViewInfos, adapter);
    }

查看 HeaderViewListAdapter 类的一些方法,可以看出 HeaderViewListAdapter 在计算子项总数和获取子项实例时,都是将顶部View和底部View包含进来的。

public int getCount() {
        if (mAdapter != null) {
            return getFootersCount() + getHeadersCount() + mAdapter.getCount();
        } else {
            return getFootersCount() + getHeadersCount();
        }
    }

public Object getItem(int position) {
        // Header (negative positions will throw an IndexOutOfBoundsException)
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {
            return mHeaderViewInfos.get(position).data;
        }

        // Adapter
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getCount();
            if (adjPosition < adapterCount) {
                return mAdapter.getItem(adjPosition);
            }
        }

        // Footer (off-limits positions will throw an IndexOutOfBoundsException)
        return mFooterViewInfos.get(adjPosition - adapterCount).data;
    }

经过以上的分析,就给我们提供了设计思路。我们同样可以设计一个包裹了 RecyclerView Adapter 和顶部底部View的外部Adapter,再将该Adapter设置给 RecyclerView。此外,由于原生的 RecyclerView 没有对应的 addHeaderView() 和 addFooterView() 方法,所以也需要再来继承 RecyclerView,在子类中定义需要的方法。

二、自定义Adapter

先定义几个需要用到的变量,headerViews 和 footerViews 分别用于存储顶部View和底部View,两个整数值则是会不断自增加一,用来作为SparseArray< View > 的Key值,adapter则是指向为RecyclerView设置的Adapter

    private RecyclerView.Adapter adapter;

    private SparseArray<View> headerViews;

    private SparseArray<View> footerViews;

    //头部类型开始位置,用于viewType
    private static int BASE_ITEM_TYPE_HEADER = 1000;

    //底部类型开始位置,用于viewType
    private static int BASE_ITEM_TYPE_FOOTER = 2000;

然后声明几个用来添加和移除View的方法

/**
     * 添加头部View
     *
     * @param view 头部View
     */
    public void addHeaderView(View view) {
        if (headerViews.indexOfValue(view) < 0) {
            headerViews.put(BASE_ITEM_TYPE_HEADER++, view);
            notifyDataSetChanged();
        }
    }

    /**
     * 添加底部View
     *
     * @param view 底部View
     */
    public void addFooterView(View view) {
        if (footerViews.indexOfValue(view) < 0) {
            footerViews.put(BASE_ITEM_TYPE_FOOTER++, view);
            notifyDataSetChanged();
        }
    }

    /**
     * 移除头部View
     *
     * @param view View
     */
    public void removeHeaderView(View view) {
        int index = headerViews.indexOfValue(view);
        if (index > -1) {
            headerViews.removeAt(index);
            notifyDataSetChanged();
        }
    }

    /**
     * 移除底部View
     *
     * @param view View
     */
    public void removeFooterView(View view) {
        int index = footerViews.indexOfValue(view);
        if (index > -1) {
            footerViews.removeAt(index);
            notifyDataSetChanged();
        }
    }

重点是 getItemViewType() 方法,如果索引值position指向的是顶部View或者底部View,则返回该View在 SparseArray< View >中的Key值,以该值作为View的 ItemViewType。如果索引指向的是中间的展示数据的子项,则调用adapter本身相同的方法

 /**
     * 根据索引判断该位置的View类型
     * 如果是头部,则返回该View在headerViews中的key
     * 如果是底部,则返回该View在footerViews中的key
     *
     * @param position 索引
     * @return View类型
     */
    @Override
    public int getItemViewType(int position) {
        if (isHeaderPosition(position)) {
            return headerViews.keyAt(position);
        }
        if (isFooterPosition(position)) {
            position = position - headerViews.size() - adapter.getItemCount();
            return footerViews.keyAt(position);
        }
        position = position - headerViews.size();
        return adapter.getItemViewType(position);
    }

为不同的View指定了不同的ItemViewType后,则可以在onCreateViewHolder() 方法中返回不同的 ViewHolder 对象了

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (isHeaderViewType(viewType)) {
            return createHeaderFooterViewHolder(headerViews.get(viewType));
        }
        if (isFooterViewType(viewType)) {
            return createHeaderFooterViewHolder(footerViews.get(viewType));
        }
        return adapter.onCreateViewHolder(parent, viewType);
    }

总的方法定义如下所示:

/**
 * 作者: 叶应是叶
 * 时间: 2017/6/4
 */
public class WrapRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private RecyclerView.Adapter adapter;

    private SparseArray<View> headerViews;

    private SparseArray<View> footerViews;

    //头部类型开始位置,用于viewType
    private int BASE_ITEM_TYPE_HEADER = 1000;

    //底部类型开始位置,用于viewType
    private int BASE_ITEM_TYPE_FOOTER = 2000;

    public WrapRecyclerViewAdapter(RecyclerView.Adapter adapter) {
        this.adapter = adapter;
        headerViews = new SparseArray<>();
        footerViews = new SparseArray<>();
        this.adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
            @Override
            public void onChanged() {
                super.onChanged();
                notifyDataSetChanged();
            }
        });
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (isHeaderViewType(viewType)) {
            return createHeaderFooterViewHolder(headerViews.get(viewType));
        }
        if (isFooterViewType(viewType)) {
            return createHeaderFooterViewHolder(footerViews.get(viewType));
        }
        return adapter.onCreateViewHolder(parent, viewType);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (isHeaderPosition(position) || isFooterPosition(position)) {
            return;
        }
        adapter.onBindViewHolder(holder, position - headerViews.size());
    }

    /**
     * 获取列表总的条数(头部View个数+列表条数+底部View个数)
     *
     * @return 总的条数
     */
    @Override
    public int getItemCount() {
        return adapter.getItemCount() + headerViews.size() + footerViews.size();
    }

    /**
     * 获取不包含头部和底部View之后列表的条数
     *
     * @return 列表条数
     */
    public int getDataItemCount() {
        return adapter.getItemCount();
    }

    /**
     * 根据索引判断该位置的View类型
     * 如果是头部,则返回该View在headerViews中的key
     * 如果是底部,则返回该View在footerViews中的key
     *
     * @param position 索引
     * @return View类型
     */
    @Override
    public int getItemViewType(int position) {
        if (isHeaderPosition(position)) {
            return headerViews.keyAt(position);
        }
        if (isFooterPosition(position)) {
            position = position - headerViews.size() - adapter.getItemCount();
            return footerViews.keyAt(position);
        }
        position = position - headerViews.size();
        return adapter.getItemViewType(position);
    }

    /**
     * 创建头部View或底部View的ViewHolder
     *
     * @param view 头部View或底部View
     * @return ViewHolder
     */
    private RecyclerView.ViewHolder createHeaderFooterViewHolder(View view) {
        return new RecyclerView.ViewHolder(view) {
        };
    }

    /**
     * 判断是否是头部View
     *
     * @param key Key
     * @return 是否是头部View
     */
    private boolean isHeaderViewType(int key) {
        return headerViews.indexOfKey(key) > -1;
    }

    /**
     * 判断是否是底部View
     *
     * @param key Key
     * @return 是否是底部View
     */
    private boolean isFooterViewType(int key) {
        return footerViews.indexOfKey(key) > -1;
    }

    /**
     * 根据索引判断该位置的View是否是头部View
     *
     * @param position 索引
     * @return 是否是头部View
     */
    private boolean isHeaderPosition(int position) {
        return (position > -1) && (position < headerViews.size());
    }

    /**
     * 根据索引判断该位置的View是否是底部View
     *
     * @param position 索引
     * @return 是否是底部View
     */
    private boolean isFooterPosition(int position) {
        return (position >= (headerViews.size() + adapter.getItemCount())) &&
                (position < (headerViews.size() + adapter.getItemCount() + footerViews.size()));
    }

    /**
     * 添加头部View
     *
     * @param view 头部View
     */
    public void addHeaderView(View view) {
        if (headerViews.indexOfValue(view) < 0) {
            headerViews.put(BASE_ITEM_TYPE_HEADER++, view);
            notifyDataSetChanged();
        }
    }

    /**
     * 添加底部View
     *
     * @param view 底部View
     */
    public void addFooterView(View view) {
        if (footerViews.indexOfValue(view) < 0) {
            footerViews.put(BASE_ITEM_TYPE_FOOTER++, view);
            notifyDataSetChanged();
        }
    }

    /**
     * 移除头部View
     *
     * @param view View
     */
    public void removeHeaderView(View view) {
        int index = headerViews.indexOfValue(view);
        if (index > -1) {
            headerViews.removeAt(index);
            notifyDataSetChanged();
        }
    }

    /**
     * 移除底部View
     *
     * @param view View
     */
    public void removeFooterView(View view) {
        int index = footerViews.indexOfValue(view);
        if (index > -1) {
            footerViews.removeAt(index);
            notifyDataSetChanged();
        }
    }

}

三、自定义RecyclerView

继承 RecyclerView 实现 WrapRecyclerView 子类 首先需要声明两个变量

    /**
     * 用来指向传入的 Adapter 或来构造 WrapRecyclerViewAdapter
     */
    private WrapRecyclerViewAdapter wrapRecyclerViewAdapter;

    /**
     * 用来指向传入的Adapter
     */
    private Adapter mRecyclerAdapter;

仿照ListView的思路来重写 setAdapter() 方法,构建一个 WrapRecyclerViewAdapter 对象作为实际的Adapter。 当中,需要注意的是,如果传入的 recyclerAdapter 是直接继承于 WrapViewRecycleAdapter 的话,则直接强转类型就可以了,否则的话需要再来根据 recyclerAdapter 构造一个 WrapViewRecycleAdapter 对象 如果 wrapRecyclerViewAdapter 是通过强转得来的,则当 mRecyclerAdapter 数据刷新时,wrapRecyclerViewAdapter 自然也会做出相应的变化,因为两者指向的是同一个对象。 如果 wrapRecyclerViewAdapter 是用new关键字重新声明的,则需要在为 mRecyclerAdapter 注册一个观察者对象,在 mRecyclerAdapter 数据刷新时同时通知 wrapRecyclerViewAdapter 也进行数据刷新。

 @Override
    public void setAdapter(Adapter recyclerAdapter) {
        if (mRecyclerAdapter != null) {
            mRecyclerAdapter.unregisterAdapterDataObserver(adapterDataObserver);
            mRecyclerAdapter = null;
        }
        mRecyclerAdapter = recyclerAdapter;
        // 如果传入的 recyclerAdapter 是直接继承于 WrapViewRecycleAdapter 的话,则直接强转类型
        // 否则的话再来根据 recyclerAdapter 构造一个 WrapViewRecycleAdapter
        if (mRecyclerAdapter instanceof WrapRecyclerViewAdapter) {
            wrapRecyclerViewAdapter = (WrapRecyclerViewAdapter) mRecyclerAdapter;
        } else {
            // 注册观察者对象
            mRecyclerAdapter.registerAdapterDataObserver(adapterDataObserver);
            wrapRecyclerViewAdapter = new WrapRecyclerViewAdapter(mRecyclerAdapter);
        }
        super.setAdapter(wrapRecyclerViewAdapter);
    }

总的代码如下所示:

/**
 * 作者: 叶应是叶
 * 时间: 2017/6/4
 * 描述: 可以带头部View与尾部View的RecyclerView
 */
public class WrapRecyclerView extends RecyclerView {

    /**
     * 用来指向传入的 Adapter 或来构造 WrapRecyclerViewAdapter
     */
    private WrapRecyclerViewAdapter wrapRecyclerViewAdapter;

    /**
     * 用来指向传入的Adapter
     */
    private Adapter mRecyclerAdapter;

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

    public WrapRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public WrapRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setAdapter(Adapter recyclerAdapter) {
        if (mRecyclerAdapter != null) {
            mRecyclerAdapter.unregisterAdapterDataObserver(adapterDataObserver);
            mRecyclerAdapter = null;
        }
        mRecyclerAdapter = recyclerAdapter;
        // 如果传入的 recyclerAdapter 是直接继承于 WrapViewRecycleAdapter 的话,则直接强转类型
        // 否则的话再来根据 recyclerAdapter 构造一个 WrapViewRecycleAdapter
        if (mRecyclerAdapter instanceof WrapRecyclerViewAdapter) {
            wrapRecyclerViewAdapter = (WrapRecyclerViewAdapter) mRecyclerAdapter;
        } else {
            // 注册观察者对象
            mRecyclerAdapter.registerAdapterDataObserver(adapterDataObserver);
            wrapRecyclerViewAdapter = new WrapRecyclerViewAdapter(mRecyclerAdapter);
        }
        super.setAdapter(wrapRecyclerViewAdapter);
    }

    /**
     * AdapterDataObserver是RecyclerView内部的一个抽象类
     * 用来作为观察者监听数据变化
     */
    private AdapterDataObserver adapterDataObserver = new AdapterDataObserver() {
        @Override
        public void onChanged() {
            if (wrapRecyclerViewAdapter != mRecyclerAdapter) {
                wrapRecyclerViewAdapter.notifyDataSetChanged();
            }
        }

        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            if (wrapRecyclerViewAdapter != mRecyclerAdapter) {
                wrapRecyclerViewAdapter.notifyItemRemoved(positionStart);
            }
        }

        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            if (wrapRecyclerViewAdapter != mRecyclerAdapter) {
                wrapRecyclerViewAdapter.notifyItemMoved(fromPosition, toPosition);
            }
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount) {
            if (wrapRecyclerViewAdapter != mRecyclerAdapter) {
                wrapRecyclerViewAdapter.notifyItemChanged(positionStart);
            }
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
            if (wrapRecyclerViewAdapter != mRecyclerAdapter) {
                wrapRecyclerViewAdapter.notifyItemChanged(positionStart, payload);
            }
        }

        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            if (wrapRecyclerViewAdapter != mRecyclerAdapter) {
                wrapRecyclerViewAdapter.notifyItemInserted(positionStart);
            }
        }
    };

    /**
     * 添加头部View
     *
     * @param view View
     */
    public void addHeaderView(View view) {
        if (wrapRecyclerViewAdapter != null) {
            wrapRecyclerViewAdapter.addHeaderView(view);
        } else {
            throw new RuntimeException("WrapRecyclerViewAdapter == null");
        }
    }

    /**
     * 添加底部View
     *
     * @param view View
     */
    public void addFooterView(View view) {
        if (wrapRecyclerViewAdapter != null) {
            wrapRecyclerViewAdapter.addFooterView(view);
        } else {
            throw new RuntimeException("WrapRecyclerViewAdapter == null");
        }
    }

    /**
     * 移除头部View
     *
     * @param view View
     */
    public void removeHeaderView(View view) {
        if (wrapRecyclerViewAdapter != null) {
            wrapRecyclerViewAdapter.removeHeaderView(view);
        } else {
            throw new RuntimeException("WrapRecyclerViewAdapter == null");
        }
    }

    /**
     * 移除底部View
     *
     * @param view View
     */
    public void removeFooterView(View view) {
        if (wrapRecyclerViewAdapter != null) {
            wrapRecyclerViewAdapter.removeFooterView(view);
        } else {
            throw new RuntimeException("WrapRecyclerViewAdapter == null");
        }
    }

}

四、实际使用

以我上一篇文章:解析RecyclerView(1)——带点击事件监听的通用Adapter 使用到的 MyCommonRecyclerAdapter 类作为最原始的Adapter

在布局文件中声明的RecyclerView就要使用自定义的 WrapRecyclerView 了,再增加几个按钮用于进行RecyclerView展示的数据,顶部View和底部View的增添删除操作。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context="com.czy.demo.RecyclerView.Wrap.WrapRecyclerActivity">

    <Button
        android:id="@+id/btn_addData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="增添数据" />

    <Button
        android:id="@+id/btn_deleteData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/btn_addData"
        android:text="删除数据" />

    <Button
        android:id="@+id/btn_addHeaderView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/btn_deleteData"
        android:text="增加头部View" />

    <Button
        android:id="@+id/btn_deleteHeaderView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/btn_addHeaderView"
        android:text="删除头部View" />

    <Button
        android:id="@+id/btn_addFooterView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/btn_deleteHeaderView"
        android:text="增加底部View" />

    <Button
        android:id="@+id/btn_deleteFooterView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/btn_addFooterView"
        android:text="删除底部View" />

    <com.czy.common.RecyclerView.Wrap.WrapRecyclerView
        android:id="@+id/wrv_dataList"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/btn_deleteFooterView" />

</RelativeLayout>

Activity总的代码如下所示,加载的顶部View布局文件 R.layout.header_view 只包含含一个 ImageView 控件,底部View布局文件 R.layout.footer _view 只包含一个 TextView 控件

public class WrapRecyclerActivity extends AppCompatActivity implements CommonRecyclerHolder.onClickCommonListener, View.OnClickListener {

    private List<Data> dataList;

    private List<View> headerViewList;

    private List<View> footerViewList;

    private WrapRecyclerView wrv_dataList;

    private MyCommonRecyclerAdapter myCommonRecyclerAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wrap_recycler);
        initData();
        wrv_dataList = (WrapRecyclerView) findViewById(R.id.wrv_dataList);
        wrv_dataList.setLayoutManager(new LinearLayoutManager(this));
        myCommonRecyclerAdapter = new MyCommonRecyclerAdapter(this, dataList, R.layout.item, this);
        wrv_dataList.setAdapter(myCommonRecyclerAdapter);
        View headerView1 = getLayoutInflater().inflate(R.layout.header_view, wrv_dataList, false);
        View headerView2 = getLayoutInflater().inflate(R.layout.header_view, wrv_dataList, false);
        View footerView1 = getLayoutInflater().inflate(R.layout.footer_view, wrv_dataList, false);
        View footerView2 = getLayoutInflater().inflate(R.layout.footer_view, wrv_dataList, false);
        wrv_dataList.addHeaderView(headerView1);
        wrv_dataList.addHeaderView(headerView2);
        wrv_dataList.addFooterView(footerView1);
        wrv_dataList.addFooterView(footerView2);
        headerViewList.add(headerView1);
        headerViewList.add(headerView2);
        footerViewList.add(footerView1);
        footerViewList.add(footerView2);
        findViewById(R.id.btn_addData).setOnClickListener(this);
        findViewById(R.id.btn_deleteData).setOnClickListener(this);
        findViewById(R.id.btn_addHeaderView).setOnClickListener(this);
        findViewById(R.id.btn_deleteHeaderView).setOnClickListener(this);
        findViewById(R.id.btn_addFooterView).setOnClickListener(this);
        findViewById(R.id.btn_deleteFooterView).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_addData:
                Data data = new Data(R.mipmap.ic_launcher, "Hi");
                dataList.add(data);
                myCommonRecyclerAdapter.notifyDataSetChanged();
                break;
            case R.id.btn_deleteData:
                if (dataList.size() > 0) {
                    dataList.remove(0);
                }
                myCommonRecyclerAdapter.notifyDataSetChanged();
                break;
            case R.id.btn_addHeaderView:
                View headerView = getLayoutInflater().inflate(R.layout.header_view, wrv_dataList, false);
                headerViewList.add(headerView);
                wrv_dataList.addHeaderView(headerView);
                break;
            case R.id.btn_deleteHeaderView:
                if (headerViewList.size() > 0) {
                    wrv_dataList.removeHeaderView(headerViewList.get(0));
                    headerViewList.remove(0);
                }
                break;
            case R.id.btn_addFooterView:
                View footerView = getLayoutInflater().inflate(R.layout.footer_view, wrv_dataList, false);
                footerViewList.add(footerView);
                wrv_dataList.addFooterView(footerView);
                break;
            case R.id.btn_deleteFooterView:
                if (footerViewList.size() > 0) {
                    wrv_dataList.removeFooterView(footerViewList.get(0));
                    footerViewList.remove(0);
                }
                break;
        }
    }

    private void initData() {
        dataList = new ArrayList<>();
        headerViewList = new ArrayList<>();
        footerViewList = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            Data data = new Data(R.mipmap.ic_launcher_round, "Hi:" + i);
            dataList.add(data);
        }
    }

    @Override
    public void onClick(int position) {
        Toast.makeText(this, "点击:" + position, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onLongClick(int position) {
        Toast.makeText(this, "长按:" + position, Toast.LENGTH_SHORT).show();
    }

}

运行效果:

这里写图片描述

这里提供代码下载:解析RecyclerView(2)——带顶部View和底部View的RecyclerView

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android知识点总结

4-AVI--Fragment与ViewPager结合

893
来自专栏Android-薛之涛

Android-Activity与Fragmeng的相互传值

方法一: 一般Framgent都不是单独存在的,所以假设如果我们从ActivityA跳转到FragmentA ,而FragmentA的宿主是MainActivi...

935
来自专栏24K纯开源

Android中的FragmentManager的问题

    Fragment是构成灵活UI的重要部分。最近学习到Fragment的使用时候,碰到一个问题。主程序程序布局非常简单: <?xml version="1...

2025
来自专栏GIS讲堂

Android数据读取之Sqlite数据库操作

咱们书接上文,继续来说说Android数据读取,这回,我们要讲的是Sqlite数据库的相关操作。以一个实例开始吧:

893
来自专栏云加新鲜事儿

Android 基础:Fragment,看这篇就够了 (上)

本文从为什么出现Fragment开始,介绍了Fragment相关的方方面面,包括Fragment的基本定义及使用、回退栈的内部实现、Fragment通信、Dia...

2K4
来自专栏程序员互动联盟

【Android基础】Fragment 详解之Fragment生命周期

Fragment的主要功能就是创建一个View,并且有一个生命周期来管理这个View的创建和销毁。Fragment的生命周期与Activity的生命周期类似,都...

3458
来自专栏向治洪

Fragment详解

##简介 A Fragment is a piece of an application’s user interface or behavior that c...

2017
来自专栏Java与Android技术栈

用kotlin打造简化版本的ButterKnife

大名鼎鼎的 ButterKnife 库相信很多 android 开发者都听过,在 Github 上star的数目已经快15k了,而且很多知名的app都在使用。

703
来自专栏青青天空树

安卓动态添加碎片

  2.   新建一个类Fragment1.java,继承自Fragment,注意Fragment有两个不同的包,推荐使用support-v4中的,兼容性更好,...

722
来自专栏开发之途

Android 解析RecyclerView(3)——以更简单的方法实现带顶部View和底部View的RecyclerView

1573

扫码关注云+社区