今天这篇文章主要是向大家介绍 RecyclerView 和 ListView 的使用对比,文章主要包括以下几点的内容:
Android 默认提供的 RecyclerView 就能支持 线性布局、网格布局、瀑布流布局 三种(这里我们暂且不提代码细节,后文再说),而且同时还能够控制横向还是纵向滚动。怎样,从效果上足以碾压 ListView 有木有。
到此,展示效果上的差距一目了然。
ListView 的基础使用大家再熟悉不过,其使用的关键点主要如下:
由于 ListView 已经老生常谈,所以此处就不去写示例代码了。 RecyclerView 基础使用关键点同样有两点:
示例代码大致如下
// 第一步:继承重写 RecyclerView.Adapter 和 RecyclerView.ViewHolder
public class AuthorRecyclerAdapter extends RecyclerView.Adapter {
...
@Override
public AuthorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
...
return viewHolder;
}
@Override
public void onBindViewHolder(AuthorViewHolder holder, int position) {
...
}
@Override
public int getItemCount() {
if (mData == null) {
return 0;
}
return mData.size();
}
class AuthorViewHolder extends RecyclerView.ViewHolder {
...
public AuthorViewHolder(View itemView) {
super(itemView);
...
}
}
}
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mRecyclerAdapter = new AuthorRecyclerAdapter(mData);
// 第二步:设置布局管理器,控制布局效果
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(RecyclerDemoActivity.this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(linearLayoutManager);
mRecyclerView.setAdapter(mRecyclerAdapter);
从基础使用上看,我们明显可以看出,RecyclerView 相比 ListView 在基础使用上的区别主要有如下几点:
而 LayoutManager 只是一个抽象类而已,系统已经为我们提供了三个相关的实现类 LinearLayoutManager(线性布局效果)、GridLayoutManager(网格布局效果)、StaggeredGridLayoutManager(瀑布流布局效果)。如果你想用 RecyclerView 来实现自己 YY 出来的一种效果,则应该去继承实现自己的 LayoutManager,并重写相应的方法,而不应该想着去改写 RecyclerView。关于 LayoutManager 的使用有下面一些常见的 API(有些在 LayoutManager 实现的子类中)
canScrollHorizontally();//能否横向滚动
canScrollVertically();//能否纵向滚动
scrollToPosition(int position);//滚动到指定位置
setOrientation(int orientation);//设置滚动的方向
getOrientation();//获取滚动方向
findViewByPosition(int position);//获取指定位置的Item View
findFirstCompletelyVisibleItemPosition();//获取第一个完全可见的Item位置
findFirstVisibleItemPosition();//获取第一个可见Item的位置
findLastCompletelyVisibleItemPosition();//获取最后一个完全可见的Item位置
findLastVisibleItemPosition();//获取最后一个可见Item的位置
上面仅仅是列出一些常用的 API 而已,更多的 API 可以查看官方文档,通常你想用 RecyclerView 实现某种效果,例如指定滚动到某个 Item 位置,但是你在 RecyclerView 中又找不到可以调用的 API 时,就可以跑到 LayoutManager 的文档去看看,基本都在那里。另外还有一点关于瀑布流布局效果 StaggeredGridLayoutManager 想说的,看到网上有些文章写的示例代码,在设置了 StaggeredGridLayoutManager 后仍要去 Adapter 中动态设置 View 的高度,才能实现瀑布流,这种做法是完全错误的,之所以 StaggeredGridLayoutManager 的瀑布流效果出不来,基本是 item 布局的 xml 问题以及数据问题导致。如果要在 Adapter 中设置 View 的高度,则完全违背了 LayoutManager 的设计理念了。
mListView = (ListView) findViewById(R.id.listview);
mListView.setEmptyView(findViewById(R.id.empty_layout));//设置内容为空时显示的视图
public class AuthorListAdapter extends BaseAdapter {
...
@Override
public View getView(int position, View convertView, ViewGroup parent) {
...
return convertView;
}
/**
* 更新Item视图,减少不必要的重绘
*
* @param listView
* @param position
*/
public void updateItemView(ListView listView, int position) {
//换算成 Item View 在 ViewGroup 中的 index
int index = position - listView.getFirstVisiblePosition();
if (index >= 0 && index < listView.getChildCount()) {
//更新数据
AuthorInfo authorInfo = mAuthorInfoList.get(position);
authorInfo.setNickName("Google Android");
authorInfo.setMotto("My name is Android .");
authorInfo.setPortrait(R.mipmap.ic_launcher);
//更新单个Item
View itemView = listView.getChildAt(index);
getView(position, itemView, listView);
}
}
}
ItemTouchHelper 是系统为我们提供的一个用于滑动和删除 RecyclerView 条目的工具类,用起来也是非常简单的,大致两步:
//ItemTouchHelper 用于实现 RecyclerView Item 拖曳效果的类
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
//actionState : action状态类型,有三类 ACTION_STATE_DRAG (拖曳),ACTION_STATE_SWIPE(滑动),ACTION_STATE_IDLE(静止)
int dragFlags = makeFlag(ItemTouchHelper.ACTION_STATE_DRAG, ItemTouchHelper.UP | ItemTouchHelper.DOWN
| ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);//支持上下左右的拖曳
int swipeFlags = makeMovementFlags(ItemTouchHelper.ACTION_STATE_SWIPE, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);//表示支持左右的滑动
return makeMovementFlags(dragFlags, swipeFlags);//直接返回0表示不支持拖曳和滑动
}
/**
* @param recyclerView attach的RecyclerView
* @param viewHolder 拖动的Item
* @param target 放置Item的目标位置
* @return
*/
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
int fromPosition = viewHolder.getAdapterPosition();//要拖曳的位置
int toPosition = target.getAdapterPosition();//要放置的目标位置
Collections.swap(mData, fromPosition, toPosition);//做数据的交换
notifyItemMoved(fromPosition, toPosition);
return true;
}
/**
* @param viewHolder 滑动移除的Item
* @param direction
*/
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();//获取要滑动删除的Item位置
mData.remove(position);//删除数据
notifyItemRemoved(position);
}
});
itemTouchHelper.attachToRecyclerView(mRecyclerView);
ListView 为我们准备了几个专门用于监听 Item 的回调接口,如单击、长按、选中某个 Item 等
说实话,其实我并不大喜欢这样的设计,如 setOnItemClickListener ,在我们不添加 HeaderView 和 FooterView 的时候,我们可以通过回调参数中的 position 去拿到数据源列表中对应 Item 的数据
并同时在 onItemClick 中判断 id 是否值为 -1,因为 HeaderView 和 FooterView 的返回值就是 -1。前面讲到我并不大喜欢 setOnItemClickListener 这种设计,除了由这些因素的影响外,更关键的是个人认为针对 Item 的事件实际上写在 getView 方法中会更加合适,如 setOnItemClickListener 我更喜欢用在 getView 中为每个 convertView 设置 setOnClickListener 的方式去取代它。
而再来看看 RecyclerView ,它并没有像 ListView 提供太多关于 Item 的某种事件监听,唯一的就是 addOnItemTouchListener
API 的名字言简意赅,就是监听 Item 的触摸事件。如果你想要拥有 ListView 那样监听某个 Item 的某个操作方法,可以看看这篇文章 RecyclerView无法添加onItemClickListener最佳的高效解决方案 ,作者的实现思路就是通过 addOnItemTouchListener 和系统提供的 GestureDetector 手势判断结合实现的。不过,我还是更喜欢原先自己用惯的方式,虽然会被人吐槽 new 出了大量的监听器,但个人觉得这样封装会更好(哈哈,也换大家吐槽这种方式的其他劣处,看看我是不是需要改改了)。
OK,关于 RecyclerView 和 ListView 一些常用的功能和 API 的对比,就大致到此。最后再来谈谈 Android L 开始之后,对 RecyclerView 和 ListView 的使用存在什么影响。