RecyclerView的基本使用方法

来源:互联网 发布:如果世上不再有猫 知乎 编辑:程序博客网 时间:2024/05/17 20:00

作为ListView的豪华加强版,Android支持库中的RecyclerView具有诸多优点。
本篇博客主要用于记录RecyclerView的基本使用方法。
当然在阅读后文前,最好对ListView及对应ViewHolder的使用有一定的了解。


一、概述
RecyclerView的关键类主要有三个,包括RecyclerView、RecyclerView.ViewHolder和RecyclerView.Adpter。
其中:
ViewHolder的作用与使用ListView时,定义的ViewHolder一致,主要用于容纳Item View的视图。
Adapter用于创建必要的ViewHolder,同时将视图和具体的数据绑定在一起。

RecyclerView需要显示视图对象时,就会去调用对应Adapter的接口,整个过程包括以下步骤:
1、调用Adapter的getItemCount方法,询问数据列表中包含多少个对象。
2、调用Adapter的createViewHolder方法,创建ViewHolder及ViewHolder要显示的视图。
3、调用Adapter的onBindViewHolder方法,将具体位置的数据与视图绑定起来,即使用视图来显示数据。

与ListView使用ViewHolder的情况一致,RecyclerView不会频繁调用createViewHolder创建ViewHolder。
当创建的ViewHolder足够多时,RecyclerView就可以回收利用旧的ViewHolder,并利用onBindViewHolder将其与
新的数据绑定,达到节约时间和内存的目的。

了解了基本的原理后,我们来看一下RecyclerView使用的例子。


二、使用方法
1、RecyclerView
RecyclerView定义于com.android.support:recyclerview-v7中,使用时需要添加该库。

使用RecyclerView时的布局类似下图:

<?xml version="1.0" encoding="utf-8"?><android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"              android:id="@+id/crime_recycler_view"              android:layout_width="match_parent"              android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>

在界面中加载RecyclerView的方式很简单,如下面代码所示:

public class CrimeListFragment extends Fragment {    ............    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,            Bundle savedInstanceState) {        //R.layout.fragment_crime_list为定义了RecyclerView的布局文件id        View v = inflater.inflate(R.layout.fragment_crime_list, container, false);        mCrimeRecyclerView = (RecyclerView) v.findViewById(R.id.crime_recycler_view);        //必须为RecyclerView设置LayoutManager        //LayoutManager将负责定义屏幕的滚动行为、定位视图等        //可以是LinearLayoutManager、GridLayoutManager等        //例如,设置格型为mCrimeRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));        mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));        ............        //RecyclerView需要显示的数据        CrimeLab crimeLab = CrimeLab.get(getActivity().getApplicationContext());        //创建RecyclerView.Adapter        mAdapter = new CrimeAdapter(crimeLab.getCrimes());        //设置Adpter        mCrimeRecyclerView.setAdapter(mAdapter);        return v;    }}

从上面的代码可以看出,RecyclerView在使用时,最主要的还是设置LayoutManager及对应的Adapter。

2、RecyclerView.Adapter
我们再看看Adapter实现的例子:

    //继承模板对象,参数为ViewHolder    private class CrimeAdapter extends RecyclerView.Adapter<CrimeHolder> {        private List<Crime> mCrimes;        //创建的时候,绑定具体的数据        public CrimeAdapter(List<Crime> crimes) {            mCrimes = crimes;        }        @Override        //onCreateViewHolder被调用时,创建具体的视图及对应的ViewHolder        public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {            //利用LayoutInflater创建出具体的视图            LayoutInflater layoutInflater = LayoutInflater.from(getActivity());            View view = layoutInflater.inflate(R.layout.list_item_crime, parent, false);            //让ViewHolder持有视图            return new CrimeHolder(view);        }        @Override        //让视图显示具体的数据        public void onBindViewHolder(CrimeHolder holder, int position) {            //这里是将显示工作丢给了ViewHolder            holder.bindCrime(mCrimes.get(position), position);        }        @Override        public int getItemCount() {            //返回数据的长度            return mCrimes.size();        }    }

Adpter这部分代码还是比较简单的,最主要的就是:
1、通过onCreateViewHolder,创建出视图给ViewHolder持有。
2、将position对应的数据,递交给ViewHolder持有的视图显示。

当数据发生变化时,可以使用Adapter的notifyDataSetChanged接口刷新整个界面;
或者使用notifyItemChanged(int position)方法刷新具体的ItemView。

3、RecyclerView.ViewHolder
最后看看ViewHolder实现的例子:

    private class CrimeHolder extends RecyclerView.ViewHolder {        ................        //ViewHolder定义成员变量保存Item View中不同的组件        public CrimeHolder(View itemView) {            super(itemView);            ..............            mTitleTextView = (TextView) itemView.findViewById(                    R.id.list_item_crime_title_text_view);            mDateTextView = (TextView) itemView.findViewById(                    R.id.list_item_crime_date_text_view);            mSolvedCrimeCheckBox = (CheckBox) itemView.findViewById(                    R.id.list_item_crime_solved_check_box);        }        //将数据显示在不同的组件上        public void bindCrime(Crime crime, int position) {            mCrime = crime;            mPosition = position;            mTitleTextView.setText(crime.getTitle());            mDateTextView.setText(crime.getDateString());            mSolvedCrimeCheckBox.setChecked(crime.isSolved());        }    }

ViewHolder的实现也极其的简单,其实就是用成员变量来持有视图中的组件。
在需要显示数据时,就将数据添加到不同的视图中。

以上就是RecyclerView的基本用法,在了解ListView使用规则的基础上,还是很好理解的。


三、可能遇到的异常

02-21 17:46:25.772 E/AndroidRuntime(30306): Process: stark.a.is.zhang.photogallery, PID: 3030602-21 17:46:25.772 E/AndroidRuntime(30306): java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 69(offset:69).state:90..............

自己写了一个应用,进行以下操作:
1、在后台更新RecyclerView对应数据的下载地址;
2、发送消息到前台,更新RecyclerView;
3、在RecyclerView绑定具体的视图时,进行实际的下载工作。
4、下载完毕时,通知主线程加载新图片。

起初,我的代码是这样写的:

//开始更新RecyclerView的数据private void reloadPictureAfterQueryChange() {    mPage = 0;    mThumbnailDownloader.clearQueue();    mLastPosition = 0;    //mGalleryItems与RecyclerView绑定    //这里先清空旧有数据    mGalleryItems.clear();    showDialog();    //这里开始重新生成新的mGalleryItems数据    startURLLoader(buildLoaderArgs());}

当界面来回滑动后,上述接口被调用时,就概率性的抛出上面提到的异常。
导致这个问题发生的根本原因是,mGalleryItems.clear后,
RecyclerView绑定的数据发生了改变,但没有收到通知。

遇到这种问题,需要进行以下修改:

    private void reloadPictureAfterQueryChange() {        mPage = 0;        mThumbnailDownloader.clearQueue();        mLastPosition = 0;        //记录旧有数据大小        int oldSize = mGalleryItems.size();        mGalleryItems.clear();        //通知RecyclerView,数据被移除了        mPhotoRecyclerView.getAdapter().notifyItemRangeRemoved(0, oldSize);        showDialog();        startURLLoader(buildLoaderArgs());    }

同样,当RecyclerView的数据增加、改变时,也要及时调用其Adapter的notifyDataSetChanged、notifyItemChanged等接口。
总之,就是让RecyclerView明确数据的变化。

0 0