Android UI组件之RecycleView

为什么提出来RecycleView?
RecyclerView提供了一种插拔式的体验,高度解耦,使用灵活。RecyclerView只管回收与复用View,通过设置LayoutManager,ItemDecoration , ItemAnimator实现各种特效,达到自由定制的目的,如实现ListView、GridView、瀑布流效果等。

有什么特点,适用场景?
优点:

标准化了ViewHolder,编写Adapter面向的是ViewHolder而不再是View了,复用的逻辑被封装了,写起来更加简单。
设置布局管理器以控制Item的布局方式,横向、竖向以及瀑布流方式。
可设置Item的间隔样式(可绘制),通过继承RecyclerView的ItemDecoration这个类,然后针对自己的业务需求去书写代码。
可以控制Item增删的动画,可以通过ItemAnimator这个类进行控制,当然针对增删的动画,RecyclerView有其自己默认的实现。
缺点:

关于Item的点击和长按事件,需要用户自己去实现。
使用成本相对高一些;
五大组成
RecyclerView由layoutManager,Adapter,ItemAnimator,ItemDecoration,ViewHolder五大核心组件。五个组件分别负责不同的功能,组合成为功能强大拓展性强的RecyclerView。

在这里插入图片描述

组件 功能
LayoutManager 负责测量和布局
Adapter 负责数据和视图的绑定
ViewHolder 视图的载体
ItemAnimator 负责Item View的动画(包括移除,增加,改变等)
ItemDecoration 负责Item View的间距控制和装饰

LayoutManager(布局管理器)
LayoutManager负责RecyclerView的布局,其中包含了Item View的获取与回收。RecyclerView提供了3种布局管理器:
1)LinearLayoutManager:以垂直或者水平列表方式展示Item
2)GridLayoutManager:以网格方式展示Item
3)StaggeredGridLayoutManager:以瀑布流方式展示Item

Adapter(适配器)

  1. 创建适配器的一般步骤
    1) 创建Adapter
    创建一个继承RecyclerView.Adapter的Adapter类(VH是ViewHolder的类名)
    2)创建ViewHolder
    在Adapter中创建一个继承RecyclerView.ViewHolder的静态内部类,记为VH。ViewHolder的实现和ListView的ViewHolder实现几乎一样。
    3)在Adapter中实现3个方法
方法 含义
onCreateViewHolder() 此方法把View直接封装在ViewHolder中,在以后使用ViewHolder代替View
onBindViewHolder() 方法提供viewHolder而不是原来的convertView,将数据设置到viewHolder中
getItemCount() 类似于BaseAdapter的getCount方法了,即总共有多少个条目

说明:可以看出,RecyclerView将ListView中getView()的功能拆分成了onCreateViewHolder()和onBindViewHolder()。

基本的Adapter实现如下:

// ① 创建Adapter
public class NormalAdapter extends RecyclerView.Adapter<NormalAdapter.VH>{
   
    //② 创建ViewHolder
    public static class VH extends RecyclerView.ViewHolder{
   
        public final TextView title;
        public VH(View v) {
   
            super(v);
            title = (TextView) v.findViewById(R.id.title);
        }
    }
    
    private List<String> mDatas;
    public NormalAdapter(List<String> data) {
   
        this.mDatas = data;
    }

    //③ 在Adapter中实现3个方法
   @Override
    public VH onCreateViewHolder(ViewGroup parent, int viewType) {
   
        //LayoutInflater.from指定写法
        View v = LayoutInflater.from(parent.getContext()).inflate(
            R.layout.item_1, parent, false);
        return new VH(v);
    }
    
    @Override
    public void onBindViewHolder(VH holder, int position) {
   
        holder.title.setText(mDatas.get(position));
        holder.itemView.setOnClickListener(new View.OnClickListener() {
   
            @Override
            public void onClick(View v) {
   
                //item 点击事件
            }
        });
    }

    @Override
    public int getItemCount() {
   
        return mDatas.size();
    }
}
  1. 万能适配器
    为了创建一个RecyclerView的Adapter,每次我们都需要去做重复劳动,包括重写onCreateViewHolder()、onBindViewHolder()、getItemCount(),并且实现过程大同小异,因此万能适配器出现了。
    我们通过public abstract class QuickAdapter extends RecyclerView.Adapter<QuickAdapter.VH>定义万能适配器QuickAdapter类,T是列表数据中每个元素的类型,QuickAdapter.VH是QuickAdapter的ViewHolder实现类,称为万能ViewHolder。
ublic abstract class QuickAdapter<T> extends RecyclerView.Adapter<QuickAdapter.VH>{
   
    private List<T> mDatas;
    public QuickAdapter(List<T> datas){
   
        this.mDatas = datas;
    }

    public abstract int getLayoutId(int viewType);

    @Override
    public VH onCreateViewHolder(ViewGroup parent, int viewType) {
   
        return VH.get(parent,getLayoutId(viewType));
    }

    @Override
    public void onBindViewHolder(VH holder, int position) {
   
        convert(holder, mDatas.get(position), position);
    }

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

    public abstract void convert(VH holder, T data, int position);
    
    static class VH extends RecyclerView.ViewHolder{
   
        private SparseArray<View> mViews;
        private View mConvertView;
    
        private VH(View v){
   
            super(v);
            mConvertView = v;
            mViews = new SparseArray<>();
        }
    
        public static VH get(ViewGroup parent, int layoutId){
   
            View convertView = LayoutInflater.from(parent.getContext()).
                inflate(layoutId, parent, false);
            return new VH(convertView);
        }
    
        public <T extends View> T getView(int id){
   
            View v = mViews.get(id);
            if(v == null){
   
                v = mConvertView.findViewById(id);
                mViews.put(id, v);
            }
            return (T)v;
        }
    }
}

1)万能ViewHolder
其中的关键点在于通过SparseArray存储item view的控件,getView(int id)的功能就是通过id获得对应的View(首先在mViews中查询是否存在,如果没有,那么findViewById()并放入mViews中,避免下次再执行findViewById())。
2)万能适配器

getLayoutId(int viewType)是根据viewType返回布局ID;
convert()做具体的bind操作;
通过万能适配器快捷创建一个Adapter:

mAdapter = new QuickAdapter<Model>(data) {
   
    @Override
    public int getLayoutId(int viewType) {
   
        switch(viewType){
   
            case TYPE_1:
                return R.layout.item_1;
            case TYPE_2:
                return R.layout.item_2;
        }
    }

    @Override
    public int getItemViewType(int position) {
   
        if(position % 2 == 0){
   
            return TYPE_1;
        } else{
   
            return TYPE_2;
        }
    }

    @Override
    public void convert(VH holder, Model data, int position) {
   
        int type = getItemViewType(position);
        switch(type){
   
            case TYPE_1:
                holder.setText(R.id.text, data.text);
                break;
            case TYPE_2:
                holder.setImage(R.id.image, data.image);
                break;
        }
    }
};

ItemDecoration(间隔样式)
作用:item之间的间隔样式。

接口定义

public static abstract class ItemDecoration {
   
    public void onDraw(Canvas c, RecyclerView parent, State state) {
   
        onDraw(c, parent);
    }
    
    public void onDrawOver(Canvas c, RecyclerView parent, State state) {
   
        onDrawOver(c, parent);
    }
    
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
   
        getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), parent);
    }
    
    @Deprecated
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
   
        outRect.set(0, 0, 0, 0);
    }
}

说明:
① onDraw方法先于drawChildren;
② onDrawOver在drawChildren之后;
③ getItemOffsets 可以通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator;
说明:系统自带的间隔样式是DividerItemDecoration。

ItemAnimator(动画)
RecyclerView能够通过mRecyclerView.setItemAnimator(ItemAnimator animator)设置添加、删除、移动、改变的动画效果。

系统提供默认实现类:DefaultItemAnimator
DefaultItemAnimator继承自SimpleItemAnimator,SimpleItemAnimator继承自ItemAnimator。DefaultItemAnimator的实现过程非常复杂,如果基于SimpleItemAnimator自定义动画类非常麻烦。

开源动画recyclerview-animators
recyclerview-animators提供了一系列的Animator,比如FadeInAnimator,ScaleInAnimator。自定义动画继承BaseItemAnimator,更加方便;

常见问题
1)局部刷新闪屏问题解决
问题描述:当Item视图中有图片和文字,当更新文字并调用notifyItemChanged()时,文字改变的同时图片会闪一下。
这个问题的原因是当调用notifyItemChanged()时,会调用DefaultItemAnimator的animateChangeImpl()执行change动画,该动画会使得Item的透明度从0变为1,从而造成闪屏。解决方便很简单禁用change动画,在rv.setAdapter()之前调用((SimpleItemAnimator)rv.getItemAnimator()).setSupportsChangeAnimations(false)。

注意事项
notifyItemInserted(position)与notifyItemRemoved(position)进行更新,否则没有动画效果。

如何使用?
使用过程
引用 --> 布局 --> 创建适配器 --> 设置RecyclerView参数 --> 增加点击事件

具体步骤介绍
引用
在build.gradle文件中引入该类:

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

布局
① Activity布局文件;
② Item的布局文件;

创建适配器
参看上面适配内容

设置RecyclerView
一般来说,需要为RecyclerView进行四大设置:
① Layout Manager(必选)
② Adapter(必选)
③ Item Decoration(可选,默认为空)
④ Item Animator(可选,默认为DefaultItemAnimator)

recyclerView = (RecyclerView) findViewById(R.id.recyclerView);  
LinearLayoutManager layoutManager = new LinearLayoutManager(this );  
layoutManager.setOrientation(OrientationHelper. VERTICAL);  
//设置布局管理器  
recyclerView.setLayoutManager(layoutManager);  
//设置Adapter  
recyclerView.setAdapter(recycleAdapter);  
 //设置分隔线  
recyclerView.addItemDecoration( new DividerGridItemDecoration(this ));  
//设置增加或删除条目的动画  
recyclerView.setItemAnimator( new DefaultItemAnimator());  

点击事件
RecyclerView并没有像ListView一样暴露出Item点击事件或者长按事件处理的api,也就是说使用RecyclerView时候,需要我们自己来实现Item的点击和长按等事件的处理。
实现方法有很多:
① 监听RecyclerView的Touch事件然后判断手势做相应的处理;
② 通过在绑定ViewHolder的时候设置监听,然后通过Apater回调出去;
我们选择第二种方法,更加直观和简单,其代码如下:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
   
    // 展示数据
    private ArrayList<String> mData;
    // 事件回调监听
    private MyAdapter.OnItemClickListener o
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值