转载请注明:
http://blog.csdn.net/sinat_30276961/article/details/50198113
今天,开始总结一下RecyclerView。本篇内容,介绍下RecyclerView,然后讲解RecyclerView的常规使用方法,包括点击事件通用添加的方法。
说到RecyclerView,我想大部分朋友都会想到ListView。没错,这两者都是用来展示一个页面一定数量的列表信息的。而且,它们的内部实现也何其相似,都是通过维护少量view来展示大量信息,这个少量view就是一页的view。
那么,为啥google在去年这么大张旗鼓的推出了RecyclerView呢?原因有几个:
1. RecyclerView把原先的ListView需要写的ViewHolder给集成进来了,我们只要复写一下就行。
2. RecyclerView把每个item的布局展示方式给解耦出来了。这样一来,灵活度就大大提高了。(通过LayoutManager的子类)
3. RecyclerView把分隔图设置也抽离出来了。通过继承ItemDecoration。
4. RecyclerView增加了添加和删除item的动画效果。你可以自定义动画效果。通过继承ItemAnimator。
从以上几点,可以看出,RecyclerView的设计充分考虑了自己定制的自由度。但由此也带来了一些麻烦,就是这些都要自己去实现。当然,很多是有现成的子类,如果够用,那就不用自己去实现了。
因为RecyclerView现在还没有加入到sdk里,所以我们要把它的jar包加进来。如果是eclipse,还需要导入一个对应的资源工程。
下面是Android Studio的添加方式:
dependencies {
compile 'com.android.support:recyclerview-v7:23.0.1'
}
然后在布局文件里,这样申明:
.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"/>
接下来,我通过一个小实例代码,展示一下RecyclerView的常规使用方式。
实例很简单,意在说明RecyclerView的基本使用方法。
先总结一下RecyclerView的常规使用方式:
1. 先获取RecyclerView对象
2. 创建一个LayoutManager对象,然后RecyclerView设置这个LayoutManager
3. 继承RecyclerView.Adapter,然后定义一个子类复写ViewHolder。
4. 创建这个Adapter对象,然后RecyclerView设置这个Adapter。
这里重点是2和3,和ListView比起来,有些区别。
ok,废话不多说,看代码:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_normal_recycler_view_test);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
// 如果可以确定每个item的高度是固定的,设置这个选项可以提高性能
mRecyclerView.setHasFixedSize(true);
// 确定每个item的排列方式
LinearLayoutManager layoutManager = new LinearLayoutManager(this) {
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
// 这里要复写一下,因为默认宽高都是wrap_content
// 这个不复写,你点击的背景色就只充满你的内容
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
};
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(layoutManager);
mAdapter = new RecyclerViewAdapter(this, R.layout.item_normal_recycler_view, 30);
mAdapter.setOnItemClickListener(new CommonAdapter.OnRecyclerViewItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(NormalRecyclerViewTest.this, "Item " + position + " click"
, Toast.LENGTH_SHORT).show();
}
});
mRecyclerView.setAdapter(mAdapter);
}
第一步就不讲了。。需要注意的是RecyclerView可以设定一个固定高度属性,这样就避免所有子View第一次高度的计算,提高了效率。
我们来看第二步,创建LayoutManager。我创建了一个LinearLayoutManager–线性排布,并设置为垂直排列。并且复写了它的generateDefaultLayoutParams。因为默认它的宽高都是设置为WRAP_CONTENT,这样一来,点击的背景高亮效果就会只覆盖一部分。如下所示:
说到LayoutManager,google已经给我们提供了三个子类:
1. LinearLayoutManager 线性排列
2. GridLayoutManager 网格排列
3. StaggeredGridLayoutManager 瀑布流排列
网格排列和瀑布流排列,会在后面再讲解。
接着说说Adapter。RecyclerView的adapter,需要复写RecyclerView.Adapter。我简单实现了一个常规的抽象Adapter。
public abstract class CommonAdapter<T extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<T>{
protected final Context mContext;
protected final int mLayoutId;
protected int mNum;
private OnRecyclerViewItemClickListener mOnItemClickListener = null;
public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
mOnItemClickListener = listener;
}
public CommonAdapter(Context context, int layoutId, int num) {
mContext = context;
mLayoutId = layoutId;
mNum = num;
}
@Override
public abstract T onCreateViewHolder(ViewGroup parent, int viewType);
@Override
public void onBindViewHolder(T holder, int position) {
holder.itemView.setOnClickListener(new OnClickListener(position, mOnItemClickListener));
}
@Override
public int getItemCount() {
return mNum;
}
public static class OnClickListener implements View.OnClickListener{
final int mPosition;
final OnRecyclerViewItemClickListener mListener;
public OnClickListener(int i, OnRecyclerViewItemClickListener listener) {
mPosition = i;
mListener = listener;
}
@Override
public void onClick(View v) {
if (mListener != null) {
mListener.onItemClick(v, mPosition);
}
}
}
// 用来设置每个item的接听
public static interface OnRecyclerViewItemClickListener {
void onItemClick(View view, int position);
}
}
增加这层抽象,主要是为了加个点击响应。懒得在每个adapter里都这样去。。
这里不得不吐槽一下RecyclerView,为了提升灵活度,它的排列方式可以实现千变万化,所带来的情况是,google觉得点击也让用户自己去实现,以匹配多变的布局。这样一来,我们只能苦逼的自己去实现点击事件了。相比较ListView,这点是一大败笔。
既然google没有添加,那只能自己实现了。于是,我就多加了一层抽象类,用于给每个item添加点击监听和回调。
我们在实现Adapter时,需要复写以下几个方法和类:
1. onCreateViewHolder 解析item布局,绑定item里的子view
2. onBindViewHolder 在这里给每个子view赋值
3. getItemCount 设置list的数量
4. RecyclerView.ViewHolder 用于保存绑定子view信息
接下去,贴出具体Adapter类:
public class RecyclerViewAdapter extends CommonAdapter<RecyclerViewAdapter.ViewHolder>{
// 临时数据
private static final int[] DRAW_IDS = new int[] {
R.drawable.cheese_1, R.drawable.cheese_2, R.drawable.cheese_3,
R.drawable.cheese_4, R.drawable.cheese_5
};
public RecyclerViewAdapter(Context context, int layoutId, int num) {
super(context, layoutId, num);
}
public void setNum(int num) {
mNum = num;
}
public int getNum() {
return mNum;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = View.inflate(mContext, mLayoutId, null);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
super.onBindViewHolder(viewHolder, i);
viewHolder.mTitle.setText("I'm a " + i + " item");
Glide.with(mContext)
.load(DRAW_IDS[i%5])
.fitCenter()
.into(viewHolder.mLogo);
// 用这种方式会卡
// viewHolder.mLogo.setImageResource(DRAW_IDS[i%5]);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private CircleImageView mLogo;
private TextView mTitle;
public ViewHolder(View itemView) {
super(itemView);
mLogo = (CircleImageView) itemView.findViewById(R.id.logo);
mTitle = (TextView) itemView.findViewById(R.id.text);
}
}
}
上面就是一个常规Adapter的写法。当然,这里我继承的是自己写的那个CommonAdapter,里面多了个点击响应。
可以看到,它把ViewHolder直接集成进来了,我们只要复写RecyclerView.ViewHolder,并在里面添加子view申明和find代码就行。
然后在onCreateViewHolder传回的值也是ViewHolder。
ok,再回到最初始的RecyclerView初始化代码:
mAdapter = new RecyclerViewAdapter(this, R.layout.item_normal_recycler_view, 30);
mAdapter.setOnItemClickListener(new CommonAdapter.OnRecyclerViewItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(NormalRecyclerViewTest.this, "Item " + position + " click"
, Toast.LENGTH_SHORT).show();
}
});
mRecyclerView.setAdapter(mAdapter);
这样一来,点击事件就绑定了,并且adapter设置完成。然后RecyclerView就可以显示了。
篇幅问题,今天就写到这。天气好冷,手残了。。
Have a good night!