android 5.0发布已经过去好久了,之前一直都没有怎么注意它。最近闲来无事,所以来学习学习。android 5.0新加了两个新的控件,RecycleView和CardView,今天,我们就一起来看看这两个控件的使用吧。


RecycleView:用于在有限的窗口集显示大量的数据

可能有的同学就要问了,我们不是已经有了ListView,GridView了吗?为什么还要新增RecycleView?难道它比ListView,GridView还要好用?哈哈哈,那是肯定的呀,要不然干嘛弄一个这个出来,既然RecycleView比ListView,GridView更厉害,那么它厉害在哪里呢?

下面我们就一起来看看吧!


整体看RecycleView的架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration,ItemAnimator实现炫酷的效果。

  • 你想要控制其显示的方式,请通过布局管理器LayoutManager
  • 你想要控制Item间的间隔(可绘制),请通过ItemDecoration
  • 你想要控制Item增删的动画,请通过ItemAnimator
  • 你想要控制点击、长按事件,请自己写(擦,这点尼玛。)

下面我们就来一个一个的讲解:

1.布局管理器LayoutManager

recycleView在LayoutManager上的最大突破就是通过一行代码来实现布局的任意切换,注意这里指的是LinearLayoutManager(类似于ListView的布局)、GridLayoutManager(类似于GridView的布局)和StaggeredGridLayoutManager这三个布局的切换。有没有感觉很牛逼。当然,写法也很简单,如下:

// 设置布局
 // LinearLayoutManager意味着这添加的是ListView这种
 // GridLayoutManager意味着这添加的是GridView这种
 // StaggeredGridLayoutManager意味着这添加的是瀑布流这种
 // recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,
                StaggeredGridLayoutManager.VERTICAL));

注意:在使用StaggeredGridLayoutManager的时候,每个Item的高度不能写成固定的,否则就体现不出瀑布流的效果,那么在使用StaggeredGridLayoutManager的时候我们需要将每个Item的高度随机生成。


2.自定义Item的分割线

不同于原来的ListView,RecycleView的分割线需要开发者自己添加,但是系统为我们提供了方法

addItemDecoration(new DividerGridItemDecoration(this));

我们只需要在DividerGridItemDecoration去定制我们自己需要的分割线就好


3.Item增删的动画

   在动画方面,系统为我们提供了一个默认的实现,方法也很简单


recyclerView.setItemAnimator(new DefaultItemAnimator());

注意,这里更新数据集不是用

adapter.notifyDataSetChanged()而是

notifyItemInserted(position)

notifyItemRemoved(position) 否则没有动画效果。在适配器里面添加这两个方法就好


public void addData(int position) {
        mDatas.add(position, "Insert One");
        notifyItemInserted(position);
    }

public void removeData(int position) {
        mDatas.remove(position);
        notifyItemRemoved(position);
    }


4.点击事件(包括长按事件)

RecycleView不像ListView系统提供了点击事件,它需要自己通过接口来实现Item的点击事件。实现方式如下:

1)在适配器里面创建一个接口(包括点击事件和长按事件,注意参数的设置)

   2)创建接口并实例化

   3)在Item的点击事件上调用接口对应的方法

   4)在主Activity的位置实现该接口


在这里我把Adapter贴出来,因为它比较关键


public  class GridAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements View.OnClickListener, View.OnLongClickListener {

private Context mContext;
private List<Meizi> datas;//数据

//自定义监听事件
public static interface OnRecyclerViewItemClickListener {
    void onItemClick(View view);
    void onItemLongClick(View view);
}

private OnRecyclerViewItemClickListener mOnItemClickListener = null;
public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
    mOnItemClickListener = listener;
}

//适配器初始化
public GridAdapter(Context context,List<Meizi> datas) {
    mContext=context;
    this.datas=datas;
}

@Override
public int getItemViewType(int position) {
    //判断item类别,是图还是显示页数(图片有URL)
    if (!TextUtils.isEmpty(datas.get(position).getUrl())) {
        return 0;
    } else {
        return 1;
    }
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
    //根据item类别加载不同ViewHolder
    if(viewType==0){
        View view = LayoutInflater.from(mContext)
                    .inflate(R.layout.grid_meizi_item, parent,false);//这个布局就是一个imageview用来显示图片
        MyViewHolder holder = new MyViewHolder(view);

       //给布局设置点击和长点击监听
        view.setOnClickListener(this);
        view.setOnLongClickListener(this);
        return holder;
    }else{
        MyViewHolder2 holder2=new MyViewHolder2(LayoutInflater.from(mContext)
                     .inflate(R.layout.page_item, parent,false));//这个布局就是一个textview用来显示页数
        return holder2;
    }

}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
 //将数据与item视图进行绑定,如果是MyViewHolder就加载网络图片,如果是MyViewHolder2就显示页数
    if(holder instanceof MyViewHolder){
        Picasso.with(mContext).load(datas.get(position).getUrl()).into(((MyViewHolder) holder).iv);//加载网络图片
    }else if(holder instanceof MyViewHolder2){
        ((MyViewHolder2) holder).tv.setText(datas.get(position).getPage()+"页");
    }

}

@Override
public int getItemCount(){
    return datas.size();//获取数据的个数
}

//点击事件回调
@Override
public void onClick(View v) {
    if (mOnItemClickListener != null) {
        mOnItemClickListener.onItemClick(v);
    }
}
@Override
public boolean onLongClick(View v) {
    if (mOnItemClickListener!= null) {
      mOnItemClickListener.onItemLongClick(v);
   }
    return false;
}

//自定义ViewHolder,用于加载图片
class MyViewHolder extends RecyclerView.ViewHolder
{
    private ImageButton iv;

    public MyViewHolder(View view)
    {
        super(view);
        iv = (ImageButton) view.findViewById(R.id.iv);
    }
}
//自定义ViewHolder,用于显示页数
class MyViewHolder2 extends RecyclerView.ViewHolder{
    private TextView tv;

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

    //添加一个item
public void addItem(Meizi meizi, int position) {
        meizis.add(position, meizi);
        notifyItemInserted(position);
        recyclerview.scrollToPosition(position);//recyclerview滚动到新加item处
    }

    //删除一个item
    public void removeItem(final int position) {
        meizis.remove(position);
        notifyItemRemoved(position);
    }
}




5.添加头部和尾部

addHeaderView(View view)和addFooterView(View view)的时候,你就会大吃一惊:什么鬼?为什么没有这两个方法。对的,在recycleView里面系统并没有提供这两个方法,那是不是意味着没有办法来添加头部和尾部呢?当然不是了,只不过添加的方式和ListView不一样而已。RecyclerView实现添加HeaderView和FooterView的核心就是在Adapter里面的onCreateViewHolder根据viewType来判断是列表项还是HeaderView来分别加载不同的布局文件,当然viewType的判断规则也是由我们定义的

public class HeaderBottomAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    //item类型
    public static final int ITEM_TYPE_HEADER = 0;
    public static final int ITEM_TYPE_CONTENT = 1;
    public static final int ITEM_TYPE_BOTTOM = 2;
    //模拟数据
    public String[] texts = {"java", "python", "C++", "Php", ".NET", "js", "Ruby", "Swift", "OC"};
    private LayoutInflater mLayoutInflater;
    private Context mContext;
    private int mHeaderCount = 1;//头部View个数
    private int mBottomCount = 1;//底部View个数

    public HeaderBottomAdapter(Context context) {
        mContext = context;
        mLayoutInflater = LayoutInflater.from(context);
    }

    //内容长度
    public int getContentItemCount() {
        return texts.length;
    }

    //判断当前item是否是HeadView
    public boolean isHeaderView(int position) {
        return mHeaderCount != 0 && position < mHeaderCount;
    }

    //判断当前item是否是FooterView
    public boolean isBottomView(int position) {
        return mBottomCount != 0 && position >= (mHeaderCount + getContentItemCount());
    }

    //判断当前item类型
    @Override
    public int getItemViewType(int position) {
        int dataItemCount = getContentItemCount();
        if (mHeaderCount != 0 && position < mHeaderCount) {
            //头部View
            return ITEM_TYPE_HEADER;
        } else if (mBottomCount != 0 && position >= (mHeaderCount + dataItemCount)) {
            //底部View
            return ITEM_TYPE_BOTTOM;
        } else {
            //内容View
            return ITEM_TYPE_CONTENT;
        }
    }

    //内容 ViewHolder
    public static class ContentViewHolder extends RecyclerView.ViewHolder {
        private TextView textView;

        public ContentViewHolder(View itemView) {
            super(itemView);
            textView = (TextView) itemView.findViewById(R.id.tv_item_text);
        }
    }

    //头部 ViewHolder
    public static class HeaderViewHolder extends RecyclerView.ViewHolder {
        public HeaderViewHolder(View itemView) {
            super(itemView);
        }
    }

    //底部 ViewHolder
    public static class BottomViewHolder extends RecyclerView.ViewHolder {
        public BottomViewHolder(View itemView) {
            super(itemView);
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == ITEM_TYPE_HEADER) {
            return new HeaderViewHolder(mLayoutInflater.inflate(R.layout.rv_header, parent, false));
        } else if (viewType == mHeaderCount) {
            return new ContentViewHolder(mLayoutInflater.inflate(R.layout.rv_item, parent, false));
        } else if (viewType == ITEM_TYPE_BOTTOM) {
            return new BottomViewHolder(mLayoutInflater.inflate(R.layout.rv_footer, parent, false));
        }
        return null;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof HeaderViewHolder) {
        } else if (holder instanceof ContentViewHolder) {
            ((ContentViewHolder) holder).textView.setText(texts[position - mHeaderCount]);
        } else if (holder instanceof BottomViewHolder) {
        }
    }

    @Override
    public int getItemCount() {
        return mHeaderCount + getContentItemCount() + mBottomCount;
    }
}



6.Item项的拖动与删除

ItemTouchHelper是一个处理RecyclerView的滑动删除和拖拽的辅助类,RecyclerView 的item拖拽移动和滑动删除就靠它来实现

itemTouchHelper=new ItemTouchHelper(new ItemTouchHelper.Callback() {

        //用于设置拖拽和滑动的方向
        @Override
        public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
            int dragFlags=0,swipeFlags=0;
            if(recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager||recyclerView.getLayoutManager() instanceof GridLayoutManager){
               //网格式布局有4个方向
               dragFlags=ItemTouchHelper.UP|ItemTouchHelper.DOWN|ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT;
            }else if(recyclerView.getLayoutManager() instanceof LinearLayoutManager){
               //线性式布局有2个方向
               dragFlags=ItemTouchHelper.UP|ItemTouchHelper.DOWN;
               swipeFlags = ItemTouchHelper.START|ItemTouchHelper.END; //设置侧滑方向为从两个方向都可以
            }
            return makeMovementFlags(dragFlags,swipeFlags);//swipeFlags 为0的话item不滑动
        }

        //长摁item拖拽时会回调这个方法
        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            int from=viewHolder.getAdapterPosition();
            int to=target.getAdapterPosition();

            Meizi moveItem=meizis.get(from);
            meizis.remove(from);
            meizis.add(to,moveItem);//交换数据链表中数据的位置

            mAdapter.notifyItemMoved(from,to);//更新适配器中item的位置
            return true;
        }

        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        //这里处理滑动删除
        }

        @Override
        public boolean isLongPressDragEnabled() {
            return false;//返回true则为所有item都设置可以拖拽
        }
    });



当然,使用的前提是进行绑定

itemTouchHelper.attachToRecyclerView(recyclerview);


如果你想为item设置拖拽和滑动时的响应动画效果,可以利用ItemTouchHelper的下面三个方法。用线性布局示例:


//当item拖拽开始时调用
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
 if(actionState==ItemTouchHelper.ACTION_STATE_DRAG){
       viewHolder.itemView.setBackgroundColor(Color.LTGRAY);//拖拽时设置背景色为灰色
    }
}

//当item拖拽完成时调用
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
 viewHolder.itemView.setBackgroundColor(Color.WHITE);//拖拽停止时设置背景色为白色
}

 //当item视图变化时调用
 @Override
 public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
 super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
  //根据item滑动偏移的值修改item透明度。screenwidth是我提前获得的屏幕宽度
  viewHolder.itemView.setAlpha(1-Math.abs(dX)/screenwidth);
 }



好了,关于RecycleView就讲解到这里,欢迎大家在下面进行评论