recyclerview

推荐:
Android RecyclerView 使用完全解析 体验艺术般的控件
高仿各大商城首页—使用分类型的RecyclerView来实现

RecyclerView是Android 5.0提出的新UI控件,可以用来代替传统的ListView、GridView。它是support-v7包中的新组件,是一个强大的滑动组件,与经典的ListView相比,同样拥有item回收复用的功能,这一点从它的名字recylerview即回收view也可以看出。

为什么会出现RecyclerView?

RecyclerView并不会完全替代ListView(这点从ListView没有被标记为@Deprecated可以看出),两者的使用场景不一样。但是RecyclerView的出现会让很多开源项目被废弃,例如横向滚动的ListView, 横向滚动的GridView, 瀑布流控件,因为RecyclerView能够实现所有这些功能。

比如有一个需求是屏幕竖着的时候的显示形式是ListView,屏幕横着的时候的显示形式是2列的GridView,此时如果用RecyclerView,则通过设置LayoutManager一行代码实现替换。

ListView vs RecyclerView

ListView相比RecyclerView,有一些优点:

  • addHeaderView(), addFooterView()添加头视图和尾视图。
  • 通过”android:divider”设置自定义分割线。
  • setOnItemClickListener()和setOnItemLongClickListener()设置点击事件和长按事件。
  • 这些功能在RecyclerView中都没有直接的接口,要自己实现(虽然实现起来很简单),因此如果只是实现简单的显示功能,ListView无疑更简单。RecycleView 和listView的一个区别就是本身不处理点击事件,点击事件应该绑在ViewHolder里面,可以直接写也可以通过接口绑在Adapter里面来实现。onCreateviewHolder函数和onBindViewHolder实现了ListView里面getView的工作,分别为找到控件和控件赋值。

RecyclerView相比ListView,有一些明显的优点:

  • 默认已经实现了View的复用,不需要类似if(convertView == null)的实现,而且回收机制更加完善。RecylerView封装了viewholder的回收复用,也就是说RecylerView标准化了ViewHolder,编写Adapter面向的是ViewHolder而不再是View了,复用的 逻辑被封装了,写起来更加简单。
  • 默认支持局部刷新。
    notifyItemRemoved(position);// 删除item
    notifyItemInserted(position);// 添加item
  • 容易实现添加item、删除item的动画效果。
    mRecyclerView.setItemAnimator(new DefaultItemAnimator());
  • 容易实现拖拽、侧滑删除等功能。
  • RecyclerView是一个插件式的实现,对各个功能进行解耦,从而扩展性比较好。提供了一种插拔式的体验,高度的解耦,异常的灵活,针对一个Item的显示RecylerView专门抽取出了相应的类,来控制Item的显示,使其的扩展性非常强。例如:你想控制横向或者纵向滑动列表效果可以通过LinearLayoutManager这个类来进行控制(与GridView效果对应的是GridLayoutManager,与瀑布流对应的还有StaggeredGridLayoutManager等),也就是说RecylerView不再拘泥于ListView的线性展示方式,它也可以实现GridView的效果等多种效果。你想控制Item的分隔线,可以通过继承RecylerView的ItemDecoration这个类,然后针对自己的业务需求去抒写代码。
mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4));
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(4,
            StaggeredGridLayoutManager.HORIZONTAL));

导入依赖:

compile 'com.android.support:recyclerview-v7:24.1.0'

方法介绍:

RecyclerView提供了notifyItemInserted(),notifyItemRemoved(),notifyItemChanged()等API更新单个或某个范围的Item视图。ListView只提供了notifyDataSetChanged()更新整个视图。

RecyclerView的Adapter与ListView的Adapter还是有点区别的,RecyclerView.Adapter,需要实现3个方法:
onCreateViewHolder()
这个方法主要生成为每个Item inflater出一个View,但是该方法返回的是一个ViewHolder。该方法把View直接封装在ViewHolder中,然后我们面向的是ViewHolder这个实例,当然这个ViewHolder需要我们自己去编写。直接省去了当初的convertView.setTag(holder)和convertView.getTag()这些繁琐的步骤。

onBindViewHolder()
这个方法主要用于适配渲染数据到View中。方法提供给你了一个viewHolder,而不是原来的convertView。

getItemCount()
这个方法就类似于BaseAdapter的getCount方法了,即总共有多少个条目。

一般在adapter的构造方法中传入上下文和数据源,并创建填充器:

public MyRecyclerAdapter(Context context, List<String> datas){  
            this. mContext=context;  
            this. mDatas=datas;  
            inflater=LayoutInflater. from(mContext);  
     }

viewholder类中,找到itemview的各个控件

  class MyViewHolder extends ViewHolder{  

           TextView tv;  

            public MyViewHolder(View view) {  
                 super(view);  
                 tv=(TextView) view.findViewById(R.id. tv_item);  
           }  

     }  

复杂一点不同item布局的需要重写getItemViewType(int position),创建不同viewtype的viewholder。通过不同的viewtype在onCreateViewHolder填充不同的布局,在onBindViewHolder设置对应的数据。点击事件可以在viewholder中写,也可以在onBindViewHolder中写,也可以使用接口暴露出去。

RecyclerView的四大组成是:

1、Adapter:为Item提供数据。

一般使用:

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.List;

/**
 * RecyclerView将ListView中getView()的功能拆分成了onCreateViewHolder()和onBindViewHolder()。
 *
 * Created by xuqiang on 2017/1/12 10:08.
 */
public class RvAdapter extends RecyclerView.Adapter<RvAdapter.VHolder> {

    private List<String> mData;

    public RvAdapter(List<String> mData) {
        this.mData = mData;
    }

    /*
    * 映射Item Layout Id,创建VH并返回。
    * */
    @Override
    public VHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_main, parent, false);//要写成这样
//        View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_main,null);//不可写成这种
        return new VHolder(view);
    }

    /*
    * 为holder设置指定数据。
    * */
    @Override
    public void onBindViewHolder(RvAdapter.VHolder holder, int position) {
        holder.mTextView.setText(mData.get(position));

        holder.mTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //item的点击事件
            }
        });
    }

    @Override
    public int getItemCount() {
        return mData.size();
    }

    public class VHolder extends RecyclerView.ViewHolder {

        private TextView mTextView;

        public VHolder(View itemView) {
            super(itemView);

            mTextView = (TextView) itemView.findViewById(R.id.mTextView);
        }
    }
}

推荐使用:

https://github.com/JoanZapata/base-adapter-helper

依赖为:

compile 'com.joanzapata.android:base-adapter-helper:1.1.11'

相关阅读:

base-adapter-helper源码解析

Android base-adapter-helper 源码分析与扩展

2、Layout Manager:Item的布局。

recyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false));//线性
recyclerView.setLayoutManager(new GridLayoutManager(this,2,LinearLayoutManager.VERTICAL,false));//网格
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL));//流式

3、Item Animator:添加、删除Item动画。

RecyclerView能够通过mRecyclerView.setItemAnimator(ItemAnimator animator)设置添加、删除、移动、改变的动画效果。RecyclerView提供了默认的ItemAnimator实现类:DefaultItemAnimator。

DefaultItemAnimator继承自SimpleItemAnimator,SimpleItemAnimator继承自ItemAnimator。

推荐使用:

https://github.com/wasabeef/recyclerview-animators

导入依赖:

compile 'jp.wasabeef:recyclerview-animators:2.2.5'

4、Item Decoration:Item之间的Divider。

RecyclerView通过addItemDecoration()方法添加item之间的分割线。Android并没有提供实现好的Divider,因此任何分割线样式都需要自己实现。

方法是:创建一个类并继承RecyclerView.ItemDecoration,重写以下两个方法:

  • onDraw(): 绘制分割线。
  • getItemOffsets(): 设置分割线的宽、高。

添加点击事件:

RecyclerView默认没有像ListView一样提供setOnItemClickListener()接口。

把点击事件的实现写在Adapter的onBindViewHolder()中,不暴露出来。

@Override
    public void onBindViewHolder(RvAdapter.VHolder holder, int position) {
        holder.mTextView.setText(mData.get(position));

        holder.mTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //item的点击事件
            }
        });
    }

添加HeaderView和FooterView

RecyclerView默认没有提供类似addHeaderView()和addFooterView()的API,因此这里介绍如何优雅地实现这两个接口。

如果你已经实现了一个Adapter,现在想为这个Adapter添加addHeaderView()和addFooterView()接口,则需要在Adapter中添加几个Item Type,然后修改getItemViewType(),onCreateViewHolder(),onBindViewHolder(),getItemCount()等方法,并添加switch语句进行判断。
在不破坏原有Adapter实现的情况下完成添加addHeaderView()和addFooterView()

添加setEmptyView

ListView提供了setEmptyView()设置Adapter数据为空时的View视图。RecyclerView虽然没提供直接的API,但是也可以很简单地实现。

  • 创建一个继承RecyclerView的类,记为EmptyRecyclerView。

  • 通过getRootView().addView(emptyView)将空数据时显示的View添加到当前View的层次结构中。

  • 通过AdapterDataObserver监听RecyclerView的数据变化,如果adapter为空,那么隐藏RecyclerView,显示EmptyView。

拖拽、侧滑删除

Android提供了ItemTouchHelper类,使得RecyclerView能够轻易地实现滑动和拖拽,此处我们要实现上下拖拽和侧滑删除。首先创建一个继承自ItemTouchHelper.Callback的类,并重写以下方法:

  • getMovementFlags():
    设置支持的拖拽和滑动的方向,此处我们支持的拖拽方向为上下,滑动方向为从左到右和从右到左,内部通过makeMovementFlags()设置。
  • onMove(): 拖拽时回调。
  • onSwiped(): 滑动时回调。
  • onSelectedChanged():
    状态变化时回调,一共有三个状态,分别是ACTION_STATE_IDLE(空闲状态),ACTION_STATE_SWIPE(滑动状态),ACTION_STATE_DRAG(拖拽状态)。此方法中可以做一些状态变化时的处理,比如拖拽的时候修改背景色。
  • clearView(): 用户交互结束时回调。此方法可以做一些状态的清空,比如拖拽结束后还原背景色。
  • isLongPressDragEnabled(): 是否支持长按拖拽,默认为true。如果不想支持长按拖拽,则重写并返回false。

RecyclerView回收机制

RecyclerView和ListView的回收机制非常相似,但是ListView是以View作为单位进行回收,RecyclerView是以ViewHolder作为单位进行回收。Recycler是RecyclerView回收机制的实现类,他实现了四级缓存:

  • mAttachedScrap: 缓存在屏幕上的ViewHolder。
  • mCachedViews: 缓存屏幕外的ViewHolder,默认为2个。ListView对于屏幕外的缓存都会调用getView()。
  • mViewCacheExtensions: 需要用户定制,默认不实现。
  • mRecyclerPool: 缓存池,多个RecyclerView共用。

参考:

Android ListView 与 RecyclerView 对比浅析–缓存机制

RecyclerView 必知必会

额外阅读:

瀑布流的使用:

    mRecyclerView = (RecyclerView) findViewById(R.id.id_recyclerview);
        mStaggeredHomeAdapter = new StaggeredHomeAdapter(this, mDatas);

        mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,
                StaggeredGridLayoutManager.VERTICAL));
        mRecyclerView.setAdapter(mStaggeredHomeAdapter);
        // 设置item动画
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());

瀑布流的adapter:


class StaggeredHomeAdapter extends
        RecyclerView.Adapter<StaggeredHomeAdapter.MyViewHolder> {

    private List<String> mDatas;
    private LayoutInflater mInflater;

    private List<Integer> mHeights;

    public interface OnItemClickLitener {
        void onItemClick(View view, int position);

        void onItemLongClick(View view, int position);
    }

    private OnItemClickLitener mOnItemClickLitener;

    public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener) {
        this.mOnItemClickLitener = mOnItemClickLitener;
    }

    public StaggeredHomeAdapter(Context context, List<String> datas) {
        mInflater = LayoutInflater.from(context);
        mDatas = datas;

        mHeights = new ArrayList<Integer>();
        for (int i = 0; i < mDatas.size(); i++) {
            mHeights.add((int) (100 + Math.random() * 300));
        }
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        MyViewHolder holder = new MyViewHolder(mInflater.inflate(
                R.layout.item_staggered_home, parent, false));
        return holder;
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int position) {
        LayoutParams lp = holder.tv.getLayoutParams();
        lp.height = mHeights.get(position);
        holder.tv.setLayoutParams(lp);// 设置item的高度

        holder.tv.setText(mDatas.get(position));

        // 如果设置了回调,则设置点击事件
        if (mOnItemClickLitener != null) {
            holder.itemView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    int pos = holder.getLayoutPosition();// 获取整个recyclerview中的position
                    mOnItemClickLitener.onItemClick(holder.itemView, pos);
                }
            });

            holder.itemView.setOnLongClickListener(new OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    int pos = holder.getLayoutPosition();// 获取整个recyclerview中的position
                    mOnItemClickLitener.onItemLongClick(holder.itemView, pos);
                    removeData(pos);
                    return false;
                }
            });
        }
    }

    @Override
    public int getItemCount() {
        return mDatas.size();
    }

    public void addData(int position) {
        mDatas.add(position, "Insert One");// 数据源增加数据
        mHeights.add((int) (100 + Math.random() * 300));// 添加一个高度
        notifyItemInserted(position);// 添加item
    }

    public void removeData(int position) {
        mDatas.remove(position);// 数据源减少数据
        notifyItemRemoved(position);// 删除item
    }

    class MyViewHolder extends ViewHolder {

        TextView tv;

        public MyViewHolder(View view) {
            super(view);
            tv = (TextView) view.findViewById(R.id.id_num);

        }
    }
}

————————-我是分割线—————————–

多类型的recyclerView的使用

     if (moduleBeanList != null) {
            //有数据
            //设置适配器
            homeRecycleAdapter = new HomeRecycleViewAdapter(mContext, moduleBeanList);
            rvHome.setAdapter(homeRecycleAdapter);

            //recycleView不仅要设置适配器还要设置布局管理者,否则图片不显示
            //第一个参数是上下文,第二个参数是只有一列
            GridLayoutManager manager = new GridLayoutManager(getActivity(), 1);

            rvHome.setLayoutManager(manager);
        }

多类型的adapter:

public class HomeRecycleViewAdapter extends RecyclerView.Adapter {
    /**
     * 4种类型
     */
    /**
     * 类型1:黑色星期五--使用banner实现
     */
    public static final int BLACK_5_BANNER0 = 0;
    /**
     * 类型2:今日新品--使用GridView实现
     */
    public static final int TODAY_NEW_GV1 = 1;
    /**
     * 类型3:品牌福利--使用ImageView实现
     */
    public static final int PIN_PAI_IV2 = 2;

    /**
     * 类型4:搭配趋势--使用RecyclerView实现
     */
    public static final int DAPEIQS_GV3 = 3;


    /**
     * 当前类型
     */
    public int currentType = BLACK_5_BANNER0;

    private final Context mContext;
    private final List<WomenBean.WomenData.ModuleBean> moduleBeanList;
    /**
     * 以后用它来初始化布局
     */
    private final LayoutInflater mLayoutInflater;

    public HomeRecycleViewAdapter(Context mContext, List<WomenBean.WomenData.ModuleBean> moduleBeanList) {
        this.mContext = mContext;
        this.moduleBeanList = moduleBeanList;
        //以后用它来初始化布局
        mLayoutInflater = LayoutInflater.from(mContext);
    }

    /**
     * 相当于getView创建ViewHolder布局
     *
     * @param parent
     * @param viewType 当前的类型
     * @return
     */
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == BLACK_5_BANNER0) {
            return new BBNViewHolder(mContext, mLayoutInflater.inflate(R.layout.banner_viewpager, null));
        } else if (viewType == TODAY_NEW_GV1) {
            return new TODAYViewHolder(mContext, mLayoutInflater.inflate(R.layout.gv_channel, null));
        } else if (viewType == PIN_PAI_IV2) {
            return new PINPAIViewHolder(mContext, mLayoutInflater.inflate(R.layout.iv_pinpai, null));
        } else if (viewType == DAPEIQS_GV3) {
            return new DaPeiViewHolder(mContext, mLayoutInflater.inflate(R.layout.dapeiqs_rv, null));
        }
        return null;
    }

    /**
     * 相当于getView中的绑定数据模块
     *
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (getItemViewType(position) == BLACK_5_BANNER0) {
            BBNViewHolder bbnViewHolder = (BBNViewHolder) holder;
            List<WomenBean.WomenData.ModuleBean.DataBean> module0data = moduleBeanList.get(0).getData();
            bbnViewHolder.setData(module0data);
        } else if (getItemViewType(position) == TODAY_NEW_GV1) {
            TODAYViewHolder todayViewHolder = (TODAYViewHolder) holder;
            List<WomenBean.WomenData.ModuleBean.DataBean> module1data = moduleBeanList.get(1).getData();
            todayViewHolder.setData(module1data);
        } else if (getItemViewType(position) == PIN_PAI_IV2) {
            PINPAIViewHolder pinpaiViewHolder = (PINPAIViewHolder) holder;
            List<WomenBean.WomenData.ModuleBean.DataBean> pinpai2data = moduleBeanList.get(2).getData();
            pinpaiViewHolder.setData(pinpai2data);
        } else if (getItemViewType(position) == DAPEIQS_GV3) {
            DaPeiViewHolder dapeiViewHolder = (DaPeiViewHolder) holder;
            List<WomenBean.WomenData.ModuleBean.DataBean> dapeiqs6data = moduleBeanList.get(6).getData();
            dapeiViewHolder.setData(dapeiqs6data);
        }
    }

    /**
     * 总共有多少个item
     *
     * @return
     */
    @Override
    public int getItemCount() {
        return 4;
    }

    /**
     * 得到类型
     */
    @Override
    public int getItemViewType(int position) {
        switch (position) {
            case BLACK_5_BANNER0:
                currentType = BLACK_5_BANNER0;
                break;
            case TODAY_NEW_GV1:
                currentType = TODAY_NEW_GV1;
                break;
            case PIN_PAI_IV2:
                currentType = PIN_PAI_IV2;
                break;
            case DAPEIQS_GV3:
                currentType = DAPEIQS_GV3;
                break;
        }
        return currentType;
    }


    class DaPeiViewHolder extends RecyclerView.ViewHolder {

        private final Context mContext;
        private RecyclerView dapeiqs_rv;

        public DaPeiViewHolder(Context mContext, View itemView) {
            super(itemView);
            this.mContext = mContext;
            dapeiqs_rv = (RecyclerView) itemView.findViewById(R.id.dapeiqs_rv);

        }

        public void setData(List<WomenBean.WomenData.ModuleBean.DataBean> dapeiqs6data) {
            //1.已有数据
            //2.设置适配器:-->设置文本和recycleView的数据
            DaPeiQSRecycleViewAdapter adapter = new DaPeiQSRecycleViewAdapter(mContext, dapeiqs6data);
            //设置秒杀的adapter
            dapeiqs_rv.setAdapter(adapter);

            //recycleView不仅要设置适配器还要设置布局管理者,否则图片不显示
            LinearLayoutManager manager = new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false);
            dapeiqs_rv.setLayoutManager(manager);

        }
    }


    class TODAYViewHolder extends RecyclerView.ViewHolder {

        private final Context mContext;
        private GridView gridView;

        public TODAYViewHolder(Context mContext, View itemView) {
            super(itemView);
            this.mContext = mContext;
            gridView = (GridView) itemView.findViewById(R.id.gv_channel);
        }

        public void setData(List<WomenBean.WomenData.ModuleBean.DataBean> module1data) {
            //已得到数据了
            //设置适配器
            TodayGVAdapter adapter = new TodayGVAdapter(mContext, module1data);
            gridView.setAdapter(adapter);
        }
    }

    static class PINPAIViewHolder extends RecyclerView.ViewHolder {
        private final Context mContext;
        @Bind(R.id.iv_new_chok)
        ImageView ivNewChok;

        PINPAIViewHolder(Context mContext, View itemView) {
            super(itemView);
            this.mContext = mContext;
            ButterKnife.bind(this, itemView);
            ivNewChok = (ImageView) itemView.findViewById(R.id.iv_new_chok);
        }

        public void setData(List<WomenBean.WomenData.ModuleBean.DataBean> pinpai2data) {
            Glide.with(mContext)
                    .load(pinpai2data.get(0).getImg())
                    .diskCacheStrategy(DiskCacheStrategy.ALL)
                    .crossFade() //设置淡入淡出效果,默认300ms,可以传参
                    .into(ivNewChok);

        }
    }


    public class BBNViewHolder extends RecyclerView.ViewHolder {

        private final Context mContext;
        private Banner banner;

        public BBNViewHolder(Context mContext, View itemView) {
            super(itemView);
            this.mContext = mContext;
            banner = (Banner) itemView.findViewById(R.id.banner);
        }

        public void setData(List<WomenBean.WomenData.ModuleBean.DataBean> module0data) {
            //设置Banner的数据
            //得到图片地址的集合
            List<String> imageUrls = new ArrayList<>();
            for (int i = 0; i < module0data.size(); i++) {
                String image = module0data.get(i).getImg();
                imageUrls.add(image);
            }

            // 222222 //新版的banner的使用----偷下懒的使用方法
            banner.setImages(imageUrls).setImageLoader(new GlideImageLoader()).start();

            //设置item的点击事件
            banner.setOnBannerClickListener(new OnBannerClickListener() {
                @Override
                public void OnBannerClick(int position) {
                    //注意这里的position是从1开始的
                    Toast.makeText(mContext, "position==" + position, Toast.LENGTH_SHORT).show();
                }
            });

        }
    }

    public class GlideImageLoader extends ImageLoader {
        @Override
        public void displayImage(Context context, Object path, ImageView imageView) {

            imageView.setScaleType(ImageView.ScaleType.FIT_XY);

            //Glide 加载图片简单用法
            Glide.with(context).load(path).into(imageView);
        }
    }


}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值