RecyclerView与ListView的对比分析

2017-11-15 11:32:04来源:http://blog.qiji.tech/archives/16436作者:技术学习小组人点击

分享
引言

最近学习了一下 RecyclerView ,感觉这个控件功能很强大啊,尤其是对于我这种常年使用 ListView 的人来说,不用网上去找各种什么瀑布流或者是横向滑动的 ListView 的开源控件去了,简直方便。下面我就从几个方面来对比一下 RecyclerView 和 ListView 。


1.基本用法

基本的使用方法上两者并没有十分明显的差别,除了布局文件,两者都需要继承重写 Adapter 、定义 ViewHolder 。但是这些工作之后, ListView 还需要进行一个 convertView 的复用的步骤,让我们通过代码来对比一下。


首先是 Listview 的代码部分:
(1)布局文件
activity_list_view_demo.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="jyx.test.demo.ListViewDemoActivity">
<ListView android:id="@+id/lv_test"
android:layout_width="match_parent" android:layout_height="wrap_content"/>
</RelativeLayout>

item_listview.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView android:id="@+id/txt_item_listview"
android:layout_width="match_parent" android:layout_height="50dp"
android:gravity="center_vertical"/>
</LinearLayout>

呃,,,貌似过于简单了,但是还是贴出来吧,对比更加全面。


(2)Activity内的代码
public class ListViewDemoActivity extends AppCompatActivity {
private ListView myListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view_demo);
myListView = (ListView) findViewById(R.id.lv_test);
myListView.setAdapter(new listViewAdapter(this));
}
//继承重写adapter
class listViewAdapter extends BaseAdapter{
private LayoutInflater mInflater;
public listViewAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return myList().size();
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
ViewHolder holder = null;
//这里要进行convertView复用的处理
if (convertView == null){
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.item_listview,null);
holder.txtItem = convertView.findViewById(R.id.txt_item_listview);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.txtItem.setText(myList().get(position).toString());
return convertView;
}
}
//ViewHolder
private class ViewHolder{
private TextView txtItem;
}
//创建一个list作为数据源
private List myList(){
List list = new ArrayList();
String item;
for (int i = 1; i < 20; i++) {
item = "这是第"+ i + "个item";
list.add(item);
}
return list;
}
}

这样,一个比较典型的ListView就完成了,效果如下: 图1.ListView效果图


接下来就是 RecyclerView 的代码部分:
(1)布局文件
activity_list_view_demo.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="jyx.test.demo.RecyclerViewDemoActivity">
<android.support.v7.widget.RecyclerView android:id="@+id/rv_test"
android:layout_width="match_parent" android:layout_height="wrap_content"/>
</RelativeLayout>

看起来似乎和 ListView 的差别不大,还是看看Activity内的代码来对比一下。


(2)Activity内的代码
public class RecyclerViewDemoActivity extends AppCompatActivity {
private RecyclerView myRecyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler_view_demo);
myRecyclerView = (RecyclerView) findViewById(R.id.rv_test);
//设置LayoutManager
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
myRecyclerView.setLayoutManager(linearLayoutManager);
//绑定adapter
myRecyclerView.setAdapter(new recyclerViewAdapter(this));
}
//创建adapter
class recyclerViewAdapter extends RecyclerView.Adapter<recyclerViewAdapter.ViewHolder>{
LayoutInflater mInflater;
public recyclerViewAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
//创建一个新的view,由LayoutManager调用
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.item_listview,parent,false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
//数据绑定控件
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.mTextView.setText(myList().get(position).toString());
}
@Override
public int getItemCount() {
return myList().size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public ViewHolder(View itemView) {
super(itemView);
mTextView = itemView.findViewById(R.id.txt_item_listview);
}
}
}
//创建一个list作为数据源
private List myList(){
List list = new ArrayList();
String item;
for (int i = 1; i < 20; i++) {
item = "这是第"+ i + "个item";
list.add(item);
}
return list;
}
}

这样,一个RecyclerView也完成了,看看效果:



图2.RecyclerView效果图


呃,,,从效果上来看,不但没什么区别,反而少了item之间的分割线,而且xml中 RecyclerView 并没有 divider 这个属性,虽然我们可以去item的布局文件中去加上分割线,但是这样看起来就太不专业了。其实 RecyclerView 是支持自定义间隔样式的,在Activity中加上这行代码:


//添加分割线
myRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));

让我们来看看效果:



图3.有分割线的RecyclerView


这只是默认的分割线,我们还可以通过继承 RecyclerView.ItemDecoration 这个抽象类来自定义分割线的样式,这次就先不做细致说明了。


这样看起来就和上面的 Listview 一模一样了。虽然外观一样了,但是通过代码的对比,我们可以总结出以下几点:


1.RecyclerView不用进行item的复用工作,这个工作Google已经做好了
2.RecyclerView的ViewHolder编写比Listview的更加规范化
3.RecyclerView需要进行LayoutManager和分割线的设定

这样看来, RecyclerView 和 ListView 也没有太大区别,但是就基本用法来说上面的效果已经可以说是 ListView 的大部分“功力”了,而 RecyclerView 还有更多效果。Android提供的 RecyclerView 除了 线性布局 的效果,还有 网格布局 和 瀑布流布局 ,而且 线性布局 还可以会横向滑动,下面我们分别简单的看一下:


①线性布局横向滑动

想让线性布局横向滑动,在上面代码的基础上,只需修改三处:


第一步,将item的xml中TextView的长宽属性调换一下,把RecyclerView控件的高改为“match_parent”(这里不做演示)


第二步,如下:


LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
//这里将Orientation设置为横向
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
myRecyclerView.setLayoutManager(linearLayoutManager);

第三步,修改分割线属性为横向分割线:


myRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL));

看下效果:



图4.横向的RecyclerView


②网格布局

代码


//这里的数字表示每行item的个数
GridLayoutManager gridLayoutManager = new GridLayoutManager(this,4);
myRecyclerView.setLayoutManager(gridLayoutManager);

效果:



图5.RecyclerView网格布局


③瀑布流布局

设置每个item的高度为随机的,可以让瀑布流看起来更高大上,下面直接上代码:


public class RecyclerViewDemoActivity extends AppCompatActivity {
private RecyclerView myRecyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler_view_demo);
myRecyclerView = (RecyclerView) findViewById(R.id.rv_test);
//设置LayoutManager
StaggeredGridLayoutManager sgLayoutManager =
new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
myRecyclerView.setLayoutManager(sgLayoutManager);
//绑定adapter
myRecyclerView.setAdapter(new recyclerViewAdapter(this));
}
//创建adapter
class recyclerViewAdapter extends RecyclerView.Adapter<recyclerViewAdapter.ViewHolder>{
LayoutInflater mInflater;
public recyclerViewAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
//创建一个新的view,由LayoutManager调用
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.item_listview,parent,false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
//数据绑定控件
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.mTextView.setText(myList().get(position).toString());
ViewGroup.LayoutParams layoutParams = holder.mTextView.getLayoutParams();
//设置item高度
layoutParams.height = (int) getHeight().get(position);
holder.mTextView.setLayoutParams(layoutParams);
}
@Override
public int getItemCount() {
return myList().size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public ViewHolder(View itemView) {
super(itemView);
mTextView = itemView.findViewById(R.id.txt_item_listview);
}
}
}
//创建一个list作为ListView的items
private List myList(){
List list = new ArrayList();
String item;
for (int i = 1; i < 20; i++) {
item = "这是第"+ i + "个item";
list.add(item);
}
return list;
}
//创建一个存放随机数200-600的list作为item高度
private List getHeight(){
List list = new ArrayList();
int height;
for (int i = 0; i < myList().size(); i++) {
height = (int) (200 + Math.random() * 400);
list.add(height);
}
return list;
}
}

来看看效果:



图6.RecyclerView的瀑布流布局


虽然没设置边框,但是可以看出高度的不同。(让我很费解的是,既然有了StaggeredGridLayoutManager,为什么还要去我们自己去设置随机的高度)。


好,基本使用的对比先到这里,我们继续对比其他方面。


2.空数据的处理

ListView为我们提供了setEmptyView这个API来让我们处理数据源为空的情况,而 RecyclerView 则没有,这个步骤需要自己来做,逻辑虽然简单,但是还是没有特别方便。


3.HeaderView和FootView

ListView可以让我们设置HeaderView和FootView,当我们想加上一个下拉刷新的或者底部加载更多的视图的时候,可以很方便的就完成了,但是 RecyclerView 就没有为我们提供这种便利,实现起来比较麻烦。希望官方以后可以继续完善。


4.item的点击事件

又是一个让人郁闷的地方, RecyclerView 并没有像 ListView 那样为我们提供短按监听OnItemClickListener()长按监听onLongClickListener,这里有两种方法,一是直接在adapter内直接为view设置监听,二是设置mRecyclerView.addOnItemTouchListener去监听然后去判断手势, 个人觉得第一种方法既简单又直观,我会选择这种方法。下面就演示一下。


ListView的item点击事件

代码如下:


myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(getBaseContext(),"短按"+myList().get(i),Toast.LENGTH_SHORT).show();
}
});
myListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(getBaseContext(),"长按"+myList().get(i),Toast.LENGTH_SHORT).show();
//这里返回true表示不会触发短按事件,false则相反
return true;
}
});

看看效果:



图7.ListView点击事件


RecyclerView的item点击事件

这里我是直接在onBindViewHolder方法中绑定的点击事件,其他地方不变:


@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
holder.mTextView.setText(myList().get(position).toString());
holder.mTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getBaseContext(),"短按"+myList().get(position),Toast.LENGTH_SHORT).show();
}
});
holder.mTextView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
Toast.makeText(getBaseContext(),"长按"+myList().get(position),Toast.LENGTH_SHORT).show();
return true;
}
});
}

看下效果:



图8.RecyclerView点击事件


这样处理貌似效果上都差不多,但是有个细节做的并不到位,就是没有动画效果,需要我们自己去代码中设置。


总结

通过这些对比,我们大概了解了两个控件的基本使用方法和区别,以后随着我们的使用,我们会更加深入了解它们的功能以及更加深入地发掘它们的使用方法。就目前来看,两个控件并没有说哪个可以完全取代另一个,各有各的优势,我们要学会灵活运用它们,这才是对比它们的意义。


最后感谢阅读,欢迎指正。


最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台