RecyclerView的概述

谷歌的官方的话语A flexible view for providing a limited window into a large data set.看翻译似乎和listView 和GridView这些列表差不多,但是在部分细节上做的似乎比listview的这些view要更好一点
简单介绍下RecyclerView的常见的几种类的

  1. RecyclerView : 总体的类,起到调控者的作用。具体的事务不是他去处理
  2. RecyclerView.Adapter : 用来将数据展示到每个item的上的,
  3. RecyclerView.Recycler : 用来回收RecyclerAdapter的item的。
  4. RecyclerView.ViewHolder: 用来管理视图的view的
  5. LayoutManager:用来决定每个item放置的位置。

从上面来看RecyclerView 从设计的过程中,就将每个功能分配到具体某项类。形成单一职责的原则。

RecyclerView的用法

RecyclerViewd的基本用法

和listView一样的。RecyclerView也需要设置适配器将数据关联上view上
具体的代码如下

//  初始化recyclerview的对象RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_main_list);RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);RecyclerView.Adapter adapter = new Adapter();// 给recyclerview的设置相应的参数recyclerView.setLayoutManager(layoutManager);recyclerView.setAdapter(adapter);

我们来看下adapter的实现的过程

class Adapter extends RecyclerView.Adapter<ListViewHolder>{        //创建一个viewholder的对象,这个方法的调用取决于视图的显示的item的个数和缓存的个数        @Override        public ListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {            View itemView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_main_list,parent,false);            return new ListViewHolder(itemView);        }        // 将取到的adapter的绑定数据,这里就不做任何处理        @Override        public void onBindViewHolder(ListViewHolder holder, int position) {        }        //item的个数        @Override        public int getItemCount() {            return 40;        }    }    // item viewholder的具体实现类    public static class ListViewHolder extends RecyclerView.ViewHolder{        public ListViewHolder(View itemView) {            super(itemView);        }    }

最后的形成的效果图为:

一些高级的用法

adapter中的getItemViewType的方法

//default 0; public int getItemViewType(int position) {    return 0; }

我们可以使用这个方法,来实现多type的item的显示,常见我们可以实现HeaderView和FooterView的RecyclerView,具体的思路如下

// 重写getItemViewTypepublic int getItemViewType(int position){    //列表中第一个item为header    if(position ==0 ){        return HEADER_TYPE;    }    //列表中最后一个item为footer    if(position == getItemCount() - 1){        return FOOTER_TYPE;    }    return LIST_TYPE;}//在onCreateViewHolder()中,根据type去创建对应的viewholderpublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {    View itemView = null;    switch (viewType){        case HEADER_TYPE:            itemView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_main_list_header, parent, false);            return new HeaderViewHolder(itemView);        case FOOTER_TYPE:            itemView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_main_list_footer, parent, false);            return new FooterViewHolder(itemView);        default:        case BODY_TYPE:            itemView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_main_list, parent, false);            return new ListViewHolder(itemView);    }}

生成效果图为:


注意 上面均没有实现onBindViewHolder()的实现方法。在实际的需要根据的业务去实现的具体的view和数据进行绑定

GridLayoutManager中的setSpanSizeLookup();

也许你会发现上面的代码对于GridLayoutManager并没有那么管用的,因为我们的在网格上的头文件的应该占据一行,而不是在网格的上在前面在占一个的,类似于下图这样的效果

这个时候我们需要将的设置一下的代码

 final GridLayoutManager manager = new GridLayoutManager(this, 4);    manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {    @Override    public int getSpanSize(int position) {        return position == 0 || position == manager.getItemCount() - 1 ? 4 : 1;    }});

形成的效果图

recyclerView的setRecycledViewPool和recyclerview的getRecycledViewPool();

recylcerview的回收机制Recycler具有两及缓存,
Scrap中的ViewHolder,不用通过Adapter重新处理,只需要attach后回到LayoutManager就可以重用。
RecycledViewPool中的ViewHolder,数据往往是错误的,则需要通过Adapter重新绑定正确的数据后在回到LayoutManager。具体的回收的机制会在回收的机制里面去说明,总之,现在只需要记住当LayoutManager需要一个新的View时

  1. Recycler会行检查scrap中是否有合适的ViewHolder,如果有直接返回给LayoutManager使用;
  2. 如果没有,就需要从Pool里面寻找,然后右Adapter重新绑定数据后,返回到LayoutManager;
  3. 如果pool还是没有,就需要由Adapter创建一个新的Viewholder
    而上面的setRecycledViewPoo()和getRecycledViewPool()的方法就是用来的让多个相同的类似的recyclerview公用一个recyclerpool的。先看一个简单的使用的方法。
//需要的能够共享的RecyclerView的设置的setRecycleChildrenOnDetach(true);从而让detach过后的viewholder放入到线程池中。RecyclerView view1 = new RecyclerView(context);LinearLayoutManager layout = new LinearLayoutManager(context);layout.setRecycleChildrenOnDetach(true);view1.setLayoutManager(layout);RecycledViewPool pool = view1.getRecycledViewPool();//将view2的recylceredViewPool()设成一个的。RecyclerView view2 = new RecyclerView(context);view2.setLayoutManager(layout);view2.setRecycledViewPool(pool);RecyclerView view3 = new RecyclerView(context);view3.setLayoutManager(layout);view3.setRecycledViewPool(pool);

实际中的viewpager的使用中的使用的情况的最多。viewpager的中多个list具有同样的viewholder,这个时候我们就可以选择的使用的setRecycledViewPool()的方法来让viewpager里面的list的都共用一个recycleredpool,我们这里以fragmentpageradpter为例

public static class PoolPagerAdapter extends FragmentPagerAdapter{    private RecyclerView.RecycledViewPool mPool = new RecyclerView.RecycledViewPool();    public PoolPagerAdapter(FragmentManager fm) {        super(fm);    }    @Override    public Fragment getItem(int position) {        RecyclerViewFragment fragment = new RecyclerViewFragment();        fragment.pool = mPool;        return fragment;    }    @Override    public int getCount() {        return 4;    }} //RecyclerViewFragment    public static class RecyclerViewFragment extends Fragment{        RecyclerView.RecycledViewPool pool;        @Nullable        @Override        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {            RecyclerView view = new RecyclerView(inflater.getContext());            LinearLayoutManager layout = new LinearLayoutManager(inflater.getContext());            layout.setRecycleChildrenOnDetach(true);            if (pool != null) {                view.setRecycledViewPool(pool);            }            view.setLayoutManager(layout);            view.setAdapter(new PoolPagerAdapter(fm));            return view;        }    }

通过以上的代码我们就可以让的viewpager里面的fragment的里面的recyclcerview的里面的都共用一个recycledpool的
同时在使用功存的recycledpool时候需要注意下面的

  1. recycledViewPool是根据getItemViewType的type值去寻找viewholder的,所以在共享的时候,需要确保RecyclerView中的adapter的getItemViewType()的返回值是不会冲突的。
  2. recycledViewPool可以自主的控制需要缓存的viewholder的数量的。setMaxRecycledViews(itemViewType, number)去设置相应的type和viewholder的数量
  3. recyclerView 是可以设置自己的所需要的viewholder的数量的。只有超出这个数量的viewholder并且detached过后才会扔进viewpool里面与其他的recyclerview共享。手动设置的数量为setItemViewCacheSize(10);
  4. 在恰当的时候,recycledViewPool会去自动清除这些viewhodlder的对象。也可以手动调用清除clear();

OnItemClickListener的实现

前文说到recyclerview其实是一个调度器,他不负责其中的量测,布局,等一些事情,它把所有的事情都交给每个类去做。它只起到宏观的调控作用。具体的实现它也不管。所以类似点击事件的监听它也不去做处理。
所以我们需要的想清楚,item的点击事件是属于谁的事务,adapter的
所以你需要在adapter中去实现监听的事件。网上这样的例子太多了,这里不做说明了。

谈谈封装的事

这里的封装,不包括将recyclerview的作为视图放在activity中,或者放在fragment中。只是对基本的recycler中的adapter进行封装。
在前文我们写了一个的adapter事对type的那种。其实想想不能这么去写。一个adapter处理两种type的事。这样是不好的。我们应该做到让一个adapter去实现一个type就行了。这样的话。后期修改方便。举个栗子 ,我们需要作出下列图表的效果。我们该如何去封装adapter呢

我们先抽出一个BaseRecyclerAdapter
E 用来的指明数据的type
T 用来指明viewholder的类型

public abstract class BaseRecyclerAdapter<E,T extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<T> {    protected Activity mActivity;    protected List<E> mDataList = new ArrayList<>();    protected OnClickListener mOnClickListener;    protected OnLongClickListener mOnLongClickListener;    public BaseRecyclerAdapter(Activity activity) {        mActivity = activity;    }    @Override    public int getItemCount() {        return mDataList.size();    }    public void setDataList(@NonNull List<E> list) {        mDataList.clear();        mDataList.addAll(list);        notifyDataSetChanged();    }    public List<E> getDataList() {        return mDataList;    }    public void setOnClickListener(OnClickListener onClickListener) {        mOnClickListener = onClickListener;    }    public void setOnLongClickListener(OnLongClickListener onLongClickListener) {        mOnLongClickListener = onLongClickListener;    }}

当然你也可以添加其他的基本的属性,这里面以最简单的实例为主。这里是以最简模式存在的,
记下来我们需要的实现的上面的效果的adapter的,在实现之前,按照我们一个之前的,我们的需要一个adapter去实现一个type的类型的viewholder的对吧,所以在这里我们需要两个的viewholder一个viewholder为名字即上面的标题,一个viewholder为列表的,并且的两个adapter的一个adapter负责处理标题的viewholder,一个adapter处理列表的viewholder,所以接下来很简单了,先准备好材料

标题的中的视图就一个textview

public class CategoryNameViewHolder extends RecyclerView.ViewHolder {    public TextView mCategoryName;    public CategoryNameViewHolder(View itemView) {        super(itemView);        mCategoryName = (TextView) itemView.findViewById(R.id.tv_item_category_list_name);    }    public CategoryNameViewHolder(Context context, ViewGroup parent) {        this(LayoutInflater.from(context).inflate(R.layout.item_category_list_name, parent, false));    }}

列表的中的list的viewholder

public class CategoryViewHolder extends RecyclerView.ViewHolder {    public TextView mListCounts;    public TextView mListName;    public RourdCornerNetworkImageView mListImage;    public View mDiviverView;    public CategoryViewHolder(View itemView) {        super(itemView);        mListCounts = (TextView) itemView.findViewById(R.id.tv_item_category_list_counts);        mListName = (TextView) itemView.findViewById(R.id.tv_item_category_list_name);        mListImage = (RourdCornerNetworkImageView) itemView.findViewById(R.id.rcniv_item_category_list_image);        mDiviverView = itemView.findViewById(R.id.view_item_category_list_divier);    }    public CategoryViewHolder(Context context, ViewGroup parent) {        this(LayoutInflater.from(context).inflate(R.layout.item_category_list, parent, false));    }}

接下来的adapter的实现。
标题的adapter 的实现。

public class CategoryNameAdapter extends BaseRecyclerAdapter<StringCategoryNameViewHolder> {    public CategoryNameAdapter(Activity activity) {        super(activity);    }    @Override    public CategoryNameViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        return new CategoryNameViewHolder(mActivity, parent);    }    @Override    public void onBindViewHolder(CategoryNameViewHolder viewHolder, int position) {        holder.mCategoryName.setText(mDataList.get(position));    }    @Override    public int getItemCount() {        return mDataList.size();    }}

列表的list的adapter的实现

public class CategoryListAdapter extends BaseRecyclerAdapter<PlayListModel> {    public CategoryListAdapter(Activity activity) {        super(activity);    }    @Override    public CategoryListAdapter onCreateViewHolder(ViewGroup parent, int viewType) {        return new CategoryViewHolder(mActivity, parent);    }    @Override    public void onBindViewHolder(CategoryListAdapter viewHolder, int position) {        onBindViewHolder(viewHolder, position, false);    }    public void onBindViewHolder(CategoryListAdapter holder, int position, boolean listEnd) {        PlayListModel playListModel = mDataList.get(position);        final String playlist_name = playListModel.getName();        final String playlist_image = playListModel.getImage();        final int playlist_counts = playListModel.getCount();        final int playlist_id = playListModel.getId();        holder.itemView.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                VideoPlayListActivity.start(mActivity, playlist_id, playlist_name, playlist_image, playlist_counts);                if(mOnClickListener!=null){                    mOnClickListener.onClick(holder.itemView,position);                }            }        });    }}

你会发现上面的四个类其实很简单,都是遵守着单一的职责的原则,一个adapter只负责处理的一个type的事。但是在我们的界面中,我们需要显示,一个标题下后面跟着它说list的,在list的结尾后会有标题。形成一种类似的通讯录的形式。但是上面的四个类只是材料,我们还需要一个耦合的adapter来处理我们的需求的。
耦合的adapter的是根据具体的需求来改动。所以在需求改动比较大的时候,我们只需要改动用来耦合的adapter就可以了。
耦合的adapter

public class VideoCategoryListAdapter extends RecyclerView.Adapter {    private Activity mActivity;    private static final int CATEGORYLIST_ADAPTER_TYPE = 2;    private static final int CATEGORY_LIST_NAME_TYPE = 1;    private CategoryListAdapter mNormalAdapter;    private CategoryNameAdapter mNameAdapter;    private List<PlayListModel> mPlayListDatas = new ArrayList<>();    private Map<Integer, Integer> mCategoryListCountsMap = new HashMap<>();    private List<String> mNames = new ArrayList<>();    public VideoCategoryListAdapter(Activity activity) {        mActivity = activity;        mNormalAdapter = new CategoryListAdapter(activity);        mNameAdapter = new CategoryNameAdapter(activity);    }    @Override    public int getItemViewType(int position) {        int count = 0;        for (int i = 0; i < mCategoryListCountsMap.size(); i++) {            if (position == count + i) {                return CATEGORY_LIST_NAME_TYPE;            }            count += mCategoryListCountsMap.get(i);        }        return CATEGORYLIST_ADAPTER_TYPE;    }    @Override    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        switch (viewType) {            case CATEGORY_LIST_NAME_TYPE:                return new CategoryNameViewHolder(mActivity, parent);            default:            case CATEGORYLIST_ADAPTER_TYPE:                return new CategoryViewHolder(mActivity, parent);        }    }    @Override    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {        int type = getItemViewType(position);        int offset = 0;        int count = 0;        boolean isSongListEnd = false;        for (int i = 0; i < mCategoryListCountsMap.size(); i++) {            if (position >= count + i) {//0+0 //5+1 //18+2                count += mCategoryListCountsMap.get(i);                offset++;            }            if (position == count + i - 1) {                isSongListEnd = true;            }        }        switch (type) {            case CATEGORY_LIST_NAME_TYPE:                mNameAdapter.onBindViewHolder(holder, offset - 1);                break;            case CATEGORYLIST_ADAPTER_TYPE:                mNormalAdapter.onBindViewHolder(holder, position - offset, isSongListEnd);                break;        }    }    @Override    public int getItemCount() {        return mNameAdapter.getItemCount() + mNormalAdapter.getItemCount();    }    public void setCategoryList(List<CategoryPlaylistModel.Categoryplaylists> categoryList) {        mPlayListDatas.clear();        mNames.clear();        List<PlayListModel> models = new ArrayList<>();        int size = categoryList.size();        for (int i = 0; i < size; i++) {            models.clear();            CategoryPlaylistModel.Categoryplaylists categoryplaylists = categoryList.get(i);            mCategoryListCountsMap.put(i, categoryplaylists.getPlaylist().size());            mNames.add(categoryplaylists.getCategory().getName());            for (PlayListModel playListModel : categoryplaylists.getPlaylist()) {                models.add(playListModel);            }            mPlayListDatas.addAll(models);        }        mNameAdapter.setDataList(mNames);        mNormalAdapter.setDataList(mPlayListDatas);        notifyDataSetChanged();    }

关于耦合的adapter 我们只需要关心在getItemViewType()的过程中的根据position 的位置去返回对应的type就行。然后在onBindViewHolder(),getItemCount(),onCreateViewHolder()等这些方法中我们只需要根据的type去分配好我们之前处理好的adapter就行了。这个一个页面的效果用这么多类似乎显得繁琐了,但是在后期的修改和维护中,绝对比一个类去实现的要好的多。这是我被坑想出来的方法。需求变化太快,我来不及一点点防备,然后我花了一天的时间,将我们的项目中的adapter全部重新构建。想到的。具体的优点

  1. 类和类之间的耦合度很低
  2. 如果你需要更改viewholder的布局,但是布局中的元素没有更改的话,那么你只需要去重写layout即可然后在viewholder里面。
  3. 如果你需要viewholder的布局,而且需要布局的元素需要更改的话,你你只需要的去重写一个viewholder,然后然后将adapter接受范性的viewholder替换掉
  4. 如果你需要修改列表的中排版,即相应位置的item的时候,你只需要去修改哪个耦合的adapter的就行了。
  5. 对应多个地方运用相同的adapter的时候,你可以直接去用。

关于recyclerview的回收机制

这个我感觉作为了解即可了,毕竟我现在作为业务层的android的。我们更多的是知道怎么回事就行了。
上源码

        View getViewForPosition(int position, boolean dryRun) {            if (position < 0 || position >= mState.getItemCount()) {                throw new IndexOutOfBoundsException("Invalid item position " + position                        + "(" + position + "). Item count:" + mState.getItemCount());            }            boolean fromScrap = false;            ViewHolder holder = null;            // 0) If there is a changed scrap, try to find from there            if (mState.isPreLayout()) {                holder = getChangedScrapViewForPosition(position);                fromScrap = holder != null;            }            // 1) Find from scrap by position 在scrap的里面寻找            if (holder == null) {                holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);                if (holder != null) {                    if (!validateViewHolderForOffsetPosition(holder)) {                        // recycle this scrap                        if (!dryRun) {                            // we would like to recycle this but need to make sure it is not used by                            // animation logic etc.                            holder.addFlags(ViewHolder.FLAG_INVALID);                            if (holder.isScrap()) {                                removeDetachedView(holder.itemView, false);                                holder.unScrap();                            } else if (holder.wasReturnedFromScrap()) {                                holder.clearReturnedFromScrapFlag();                            }                            recycleViewHolderInternal(holder);                        }                        holder = null;                    } else {                        fromScrap = true;                    }                }            }            if (holder == null) {                final int offsetPosition = mAdapterHelper.findPositionOffset(position);                if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {                    throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "                            + "position " + position + "(offset:" + offsetPosition + ")."                            + "state:" + mState.getItemCount());                }                final int type = mAdapter.getItemViewType(offsetPosition);                // 2) Find from scrap via stable ids, if exists                if (mAdapter.hasStableIds()) {                    holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);                    if (holder != null) {                        // update position                        holder.mPosition = offsetPosition;                        fromScrap = true;                    }                }                if (holder == null && mViewCacheExtension != null) {                    // We are NOT sending the offsetPosition because LayoutManager does not                    // know it.                    final View view = mViewCacheExtension                            .getViewForPositionAndType(this, position, type);                    if (view != null) {                        holder = getChildViewHolder(view);                        if (holder == null) {                            throw new IllegalArgumentException("getViewForPositionAndType returned"                                    + " a view which does not have a ViewHolder");                        } else if (holder.shouldIgnore()) {                            throw new IllegalArgumentException("getViewForPositionAndType returned"                                    + " a view that is ignored. You must call stopIgnoring before"                                    + " returning this view.");                        }                    }                }                if (holder == null) { // fallback to recycler                    // try recycler.                    // Head to the shared pool.                    if (DEBUG) {                        Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "                                + "pool");                    }                    holder = getRecycledViewPool().getRecycledView(type);                    if (holder != null) {                        holder.resetInternal();                        if (FORCE_INVALIDATE_DISPLAY_LIST) {                            invalidateDisplayListInt(holder);                        }                    }                }                if (holder == null) {                    holder = mAdapter.createViewHolder(RecyclerView.this, type);                    if (DEBUG) {                        Log.d(TAG, "getViewForPosition created new ViewHolder");                    }                }            }            boolean bound = false;            if (mState.isPreLayout() && holder.isBound()) {                // do not update unless we absolutely have to.                holder.mPreLayoutPosition = position;            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {                if (DEBUG && holder.isRemoved()) {                    throw new IllegalStateException("Removed holder should be bound and it should"                            + " come here only in pre-layout. Holder: " + holder);                }                final int offsetPosition = mAdapterHelper.findPositionOffset(position);                holder.mOwnerRecyclerView = RecyclerView.this;                mAdapter.bindViewHolder(holder, offsetPosition);                attachAccessibilityDelegate(holder.itemView);                bound = true;                if (mState.isPreLayout()) {                    holder.mPreLayoutPosition = position;                }            }            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();            final LayoutParams rvLayoutParams;            if (lp == null) {                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();                holder.itemView.setLayoutParams(rvLayoutParams);            } else if (!checkLayoutParams(lp)) {                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);                holder.itemView.setLayoutParams(rvLayoutParams);            } else {                rvLayoutParams = (LayoutParams) lp;            }            rvLayoutParams.mViewHolder = holder;            rvLayoutParams.mPendingInvalidate = fromScrap && bound;            return holder.itemView;        }

基本的流程我们前面已经说过了。

更多相关文章

  1. SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
  2. 一句话锁定MySQL数据占用元凶
  3. 解决WebView加载Https无法显示的问题
  4. Android(安卓)ListView拖动时,背景颜色会变成黑色
  5. Android(安卓)studio 3.0安装配置方法图文教程
  6. 打开Android(安卓)PVLogger的方法
  7. Android_SQLite数据库详解
  8. 学习ANDROID GOOGLE地图5部曲
  9. Android数据存储操作

随机推荐

  1. [每日100问][2011-10-01]iphone开发笔记,
  2. 推荐一个android学习网站
  3. Android开发环境搭建流程
  4. Android(安卓)IOS WebRTC 音视频开发总结
  5. ANDROID相对布局详细说明
  6. Android用户界面 UI组件--TextView及其子
  7. Android(安卓)Studio如何显示行号
  8. Android中如何用好多线程
  9. Android消息处理机制(Handler)
  10. Android_RelativeLayout属性