Android5.0 RecyclerView的使用
RecylerView简介
RecylerView是一个高级的ListView。可以很好的维护大数据集的滚动和显示。
RecylerView位置
包名:android.support.v7.widget.RecyclerView
文件地址有两个
1: android-sdk/extras/android/m2repository/com/android/support/recyclerview-v7
2:android-sdk/extras/android/support/v7/recyclerview
RecylerView引用
Android Studio
dependencies {
compile 'com.android.support:recyclerview-v7:21.0.0'
}
Eclipse
1.在android-sdk/extras/android/support/v7/recyclerview目录下面有libs,里面有jar包,引用此jar包。
2.在android-sdk/extras/android/m2repository/com/android/support/recyclerview-v7目录下根据版本号21.0.0目录可以找到一个名为recyclerview-v7-21.0.0.aar的文件。解压此文件里面有classes.jar,引用此jar包。
针对找不到目录,可以打开Android SDK Manager把最新的资源更新下来即可。
在此推荐使用Android Studio开发Android项目
为什么要使用RecyclerView
RecyclerView适用于无法在一个屏幕范围内展现格式一样的数据时,需要用多行或多列来展示。
比如,当用户滑动使当前一个可视的Item滑出屏幕,这个Item的视图将会被回收并在一个新Item进入可视范围后重新被使用。
可回收利用View是个很实用的功能,它不仅可以减少CPU不断Inflate View的开销,而且可以节省缓存View的内存开销。
ListView具备这样的机制,但是当使用ListView时,显示,回收等功能是紧密耦合在一起的。
RecyclerView不负责显示工作。和ListView不一样的是,RecyclerView不再负责Item的摆放等显示方面的功能。所有和布局、绘制等方面的工作Google都其拆分成不同的类进行管理。所以我们可以自定义各种各样满足定制需求的的功能类。
RecylerView类的说明
涉及到的类
Adapter(android.support.v7.widget.RecyclerView.Adapter)
ViewHolder(android.support.v7.widget.RecyclerView.ViewHolder)
LayoutManager(android.support.v7.widget.RecyclerView.LayoutManager)
Adapter
适配器,跟ListView有关的Adapter是不一样,此Adapter为RecylerView特有。作为一个抽象类,有以下几个抽象方法。
public static abstract class Adapter<VH extends ViewHolder> {
public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
public abstract void onBindViewHolder(VH holder, int position);
public abstract int getItemCount();
...
}
1. onCreateViewHolder
直接创建一种可复用的VH或者根据ViewType创建多种VH。
2. onBindViewHolder
数据和VH通过位置position绑定
3. getItemCount
返回有多少条数据
4. ViewHolder
同样是一个抽象类,我们通过继承此类实现view的封装。
5. LayoutManager
布局管理器,RecylerView中数据显示布局方式。目前v7包种提供了三种模式,分别是LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager。
LinearLayoutManager
线性布局,通过方向VERTICAL和HORIZONTAL可以实现垂直和水平的效果。默认为VERTICAL垂直方向。
通过此布局可以实现ListView的效果。垂直方向为普通的ListView显示效果,水平方向即是水平滑动的ListView。
GridLayoutManager
网格布局,继承于LinearLayoutManager,可以指定有几行和方向。
通过此布局可以实现GridView的效果,同样有垂直方向和水平方向。
StaggeredGridLayoutManager
交错网格布局,类似于网格布局,但每个格子的高度或者长度可以不一样。俗称的瀑布流效果,同样有垂直方向和水平方向。
实例代码
引入的包
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:21.0.0'
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.3'
compile 'com.android.support:recyclerview-v7:21.0.0'
compile 'org.roboguice:roboguice:2.0'
compile 'com.android.support:palette-v7:21.0.0'
}
MainActivity.java
package com.cienet.android;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import com.cienet.android.adapter.MyAdapter;
import roboguice.activity.RoboFragmentActivity;
import roboguice.inject.InjectView;
public class MainActivity extends RoboFragmentActivity {
@InjectView(R.id.recyclerView)
private RecyclerView recyclerView;
@InjectView(R.id.swipeLayout)
private SwipeRefreshLayout swipeLayout;
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
adapter = new MyAdapter(this, getPicUrls());
useLinearLayoutManager();
// useGridLayoutManager();
// useStaggeredGridLayoutManager();
recyclerView.setAdapter(adapter);
// 模拟下拉刷新
swipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
swipeLayout.setRefreshing(false);
adapter.notifyDataSetChanged();
}
}, 2000);
}
});
}
/**
* 使用线性布局管理器
* @return
*/
private void useLinearLayoutManager() {
// 创建线性布局管理器
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
// 设置显示布局的方向,默认方向是垂直
// linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
// 设置布局管理器
recyclerView.setLayoutManager(linearLayoutManager);
}
/**
* 使用网格布局管理器
* @return
*/
private void useGridLayoutManager() {
// 创建网格布局管理器
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
// 设置显示布局的方向,默认方向是垂直
// gridLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
// 设置布局管理器
recyclerView.setLayoutManager(gridLayoutManager);
}
/**
* 使用交错网格布局管理器
* @return
*/
private void useStaggeredGridLayoutManager() {
// 创建交错网格布局管理器
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(2, LinearLayoutManager.VERTICAL);
// 设置布局管理器
recyclerView.setLayoutManager(staggeredGridLayoutManager);
}
private String[] getPicUrls() {
String[] picUrls = new String[]{
"http://img0.hao123.com/data/1_2d6066fea769896573b01478c2312832_510",
"http://img4.hao123.com/data/1_dd84959fa7910d741d0f4cc9dec79bdd_510",
"http://img6.hao123.com/data/1_72b86760cbcf0a9251e4c5d28127d3f6_510",
"http://img3.hao123.com/data/1_ae18d3a1b65a0194a8f5fa2c76f3f8a7_0",
"http://img5.hao123.com/data/1_bc6ef28f063aa1c0d72daed48a18554a_0",
"http://img.hao123.com/data/1_62333db73d9fa2fe2a1db6f26edab9f3_0",
"http://img.hao123.com/data/1_0c4f1dc3daab007063fac855c9825ca5_0",
"http://img6.hao123.com/data/1_22699180ce1bfef7db27c205a3b9cda2_0",
"http://img4.hao123.com/data/1_758d06615bb089bcc979aa974442720a_0",
"http://img.hao123.com/data/1_bf80a0aa0901e1a52ba2cd03c164511e_0"
};
return picUrls;
}
}
MyAdapter.java
package com.cienet.android.adapter;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.cienet.android.R;
import com.nostra13.universalimageloader.core.ImageLoader;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private String[] picUrls;
public MyAdapter(Context context, String[] picUrls) {
this.picUrls = picUrls;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
// 加载数据item的布局,生成VH返回
if (null == viewGroup.getTag()) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_vertical, viewGroup, false);
// View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_horizontal, viewGroup, false);
// View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_gridlayout, viewGroup, false);
// View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_staggered_gridlayout, viewGroup, false);
viewGroup.setTag(view);
return new MyViewHolder(view);
} else {
return new MyViewHolder((View) viewGroup.getTag());
}
}
@Override
public void onBindViewHolder(MyViewHolder viewHolder, int i) {
// 数据绑定
ImageLoader.getInstance().displayImage(picUrls[i], viewHolder.picImageView);
viewHolder.picUrl.setText(picUrls[i]);
}
@Override
public int getItemCount() {
// 返回数据有多少条
if (null == picUrls) {
return 0;
}
return picUrls.length;
}
// 可复用的VH
public class MyViewHolder extends RecyclerView.ViewHolder {
public ImageView picImageView;
public TextView picUrl;
public MyViewHolder(View itemView) {
super(itemView);
picImageView = (ImageView) itemView.findViewById(R.id.imavPic);
picUrl = (TextView) itemView.findViewById(R.id.tvUrl);
}
}
}
activity_main.xml
<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"
android:background="@android:color/holo_red_dark"
tools:context=".MainActivity">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
item_vertical.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="match_parent"
android:layout_margin="5dp"
android:orientation="vertical">
<!-- 垂直时候使用的ImageView控件-->
<ImageView
android:id="@+id/imavPic"
android:layout_width="wrap_content"
android:layout_height="150dp"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/tvUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lines="2" />
</LinearLayout>
item_horizontal.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="match_parent"
android:layout_margin="5dp"
android:orientation="vertical">
<!-- 水平时候使用的ImageView控件-->
<ImageView
android:id="@+id/imavPic"
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/tvUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lines="2" />
</LinearLayout>
item_gridlayout.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="match_parent"
android:layout_margin="5dp"
android:orientation="vertical">
<ImageView
android:id="@+id/imavPic"
android:layout_width="match_parent"
android:layout_height="150dp"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/tvUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lines="2" />
</LinearLayout>
item_staggered_gridlayout.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="match_parent"
android:layout_margin="5dp"
android:orientation="vertical">
<ImageView
android:id="@+id/imavPic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/tvUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
自己运行验证结果,这里就不上图了。
与ListView的相同与不同
其实官方文档说,RecyclerView是ListView的豪华增强版。它主要包含以下几处新的特性,
如ViewHolder,ItemDecorator,LayoutManager,SmothScroller以及增加或删除item时item动画等。
并且官方推荐我们采用RecyclerView来取代ListView。
1. ViewHolder
ViewHolder是用来保存视图引用的类,无论是ListView亦或是RecyclerView。只不过在ListView中,
ViewHolder需要自己来定义,且这只是一种推荐的使用方式,不使用当然也可以,这不是必须的。
只不过不使用ViewHolder的话,ListView每次getView的时候都会调用findViewById(int),
这将导致ListView性能展示迟缓。而在RecyclerView中使用RecyclerView.ViewHolder则变成了必须,
尽管实现起来稍显复杂,但它却解决了ListView面临的上述不使用自定义ViewHolder时所面临的问题。
RecyclerView.ViewHolder被BaseAdapter使用,以将posiiton绑定到上面
(可以通过API查看 RecyclerView.ViewHolder#getPosition() 方法)。
2. LayoutManager
ListView只能在垂直方向上滚动,Android API没有提供ListView在水平方向上面滚动的支持。
或许有多种方式实现水平滑动,但是ListView并不是设计来做这件事情的。而RecyclerView相较于
ListView,在滚动上面的功能扩展了许多。它可以支持多种类型列表的展示要求。
3. ItemAnimator
Android API中,删除或添加item时,item是无法产生动画效果的。相比较于ListView,
RecyclerView.ItemAnimator则被提供用于在RecyclerView添加、删除或移动item时处理动画效果。
同时,不想自定义ItemAnimator,可以使用已有的 DefaultItemAnimator 。
4. Adapter
ListView的Adapter中,getView是最重要的方法,它将视图跟position绑定起来。
同时我们也能够通过registerDataObserver在Adapter中注册一个观察者。RecyclerView也有这个特性,
RecyclerView.AdapterDataObserver就是这个观察者。ListView有三个Adapter的默认实现,
分别是ArrayAdapter、CursorAdapter和SimpleCursorAdapter。然而,RecyclerView的Adapter则
拥有除了内置的内DB游标和ArrayList的支持之外的所有功能。 RecyclerView.Adapter 的实现的,
我们必须采取措施将数据提供给Adapter,正如BaseAdapter对ListView所做的那样。
5. ItemDecoration
在ListView中如果我们想要在item之间添加间隔符,我们只需要在布局文件中对ListView添加如下属性即可:
1 android:divider="@android:color/transparent"
2 android:dividerHeight="5dp"
RecyclerView在默认情况下并不在item之间展示间隔符。这个问题增加了我们的负担。如果你想要添加间隔符,
你必须使用RecyclerView.ItemDecoration类来实现。或者,可以参考应用官方示例中的 DividerItemDecoration.java文件。
6. OnItemTouchListener
ListView通过AdapterView.OnItemClickListener接口来探测点击事件。而RecyclerView则通过
RecyclerView.OnItemTouchListener接口来探测触摸事件。它虽然增加了实现的难度,但是却给予我们拦截触摸事件
更多的控制权限。
Others
ListView可以设置选择模式,并添加MultiChoiceModeListener,如下所示:
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
...
}
public void onItemCheckedStateChanged(ActionMode mode, int position,
long id, boolean checked) {
...
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_delete_crime:
CrimeAdapter adapter = (CrimeAdapter)getListAdapter();
CrimeLab crimeLab = CrimeLab.get(getActivity());
for (int i = adapter.getCount() - 1; i >= 0; i--) {
if (getListView().isItemChecked(i)) {
crimeLab.deleteCrime(adapter.getItem(i));
}
}
mode.finish();
adapter.notifyDataSetChanged();
return true;
default:
return false;
}
public boolean onPrepareActionMode(ActionMode mode, Menu menu)
...
}
public void onDestroyActionMode(ActionMode mode) {
...
}
});
而RecyclerView则没有此功能。
总之,通过比较我们可以发现,RecyclerView充满了大量的自定义功能,它可以用于实现复杂的列表或网格,但实现起来稍显得复杂。