您的位置:首页 > 其它

识更加强大的滚动控件——RecyclerView

2017-09-08 01:29 253 查看
为什么要使用RecyclerView?

既然在安卓中已经有了ListView和GridView这样的控件,那么我们为什么还要去使用RecyclerView这个控件呢。首先的一点就在于之前的ListView和GridView的性能并不是非常好。尽管两者都有自己的缓存机制,但还是有可以改进的空间,比如使用ViewHolder来避免过多使用findViewById这样一个非常耗时的操作。而在RecyclerView中,直接将ViewHolder作为RecyclerView 的一个子类,强制开发者使用它进行代码的优化。当然最简单的原因就是谷歌喜欢。。

RecyclerView 如何使用

RecyclerView只关注与item的创建与回收,对于item的布局方式、item的点击事件处理、动画效果、item分隔等等一律不关注,这就是为什么RecyclerView的流畅度特别好的原因。

1. 申明布局控件

同ListView相似,首先需要在布局文件中申明RecyclerView这个控件。因为该控件是谷歌新推出的一个控件,为了兼容之前的安卓版本,所以这个控件是放在support.v7的兼容库当中,因此需要添加相应的依赖,并且在引用时需要写完整的包名。
compile 'com.android.support:recyclerview-v7:24.2.1'

<android.support.v7.widget.RecyclerView
android:id="@+id/id_recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />


2. 在代码中引用控件,自定义item布局
//在Activity中通过findViewById来找到控件
recycleView = (RecyclerView) findViewById(R.id.id_recyclerView);


建立自定义的item布局和ListView中过程完全一致,在此不贴代码了,附上效果图



3. 新建Adapter

根据demo的内容,新建DramaAdapter继承自RecyclerView.Adapter,泛型为自定义的继承自RecyclerView.ViewHolder的内部类DramaHolder,其父类中已经有了一个元素itemView对应列表中的每一个item,所以我们自己只需要在holder中添加item布局中的每个控件,比如TextView,ImageView等等。同时需要复写自定义ViewHolder的构造方法,通过findViewById的方法将holder的各个属性与相应的控件绑定在一起,这样就可以避免过多的findViewById的操作,例如在我的demo中:
public DramaHolder(View itemView) {
super(itemView);
icon = (ImageView) itemView.findViewById(R.id.item_icon);
name = (TextView) itemView.findViewById(R.id.item_name);
progress = (TextView) itemView.findViewById(R.id.item_progress);
}

在Adapter中有三个方法需要复写,getItemCount,onCreateViewHolder,onBindViewHolder


在Adapter中应有List类型成员变量,用于接收Activity中的数据源,getItemCount返回List的长度即可,例如:
@Override
public int getItemCount() {
return mDramaList.size();
}


Adapter中最为关键的两个方法就是onCreateViewHolder和onBindViewHolder,这两个方法的作用相当与我们在使用ListView自定义Adapter继承自BaseAdapter时复写的getView的方法作用是一致的,但是有所区别的就是ListView所接收的都是View类型的对象,而RecyclerView接收的都是ViewHolder对象。

在onCreateViewHolder方法中通过LayoutInflater将自定义的item布局转为View对象,再将该对象与一个Viewholder相关联,并返回该ViewHolder。
@Override
public DramaAdapter.DramaHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_list_item,parent,false);
DramaHolder holder = new DramaHolder(view);
return holder;
}


onBindViewHolder方法是真正将数据源与视图关联起来的函数所在了,根据传入的position拿到数据源中的item的各项数据,并通过holder的各个属性设置相应的数据,完成Adapter的功能。
@Override
public void onBindViewHolder(final DramaAdapter.DramaHolder holder, int position) {
Drama drama = mDramaList.get(position);
holder.icon.setImageResource(drama.getIconId());
holder.name.setText(drama.getName());
holder.progress.setText(drama.getProgress());
}


4. 设置RecyclerView的布局方式

我们现在所完成的工作是将数据与视图进行绑定,但是不要忘了我们之前所说的RecyclerView只关注于item的创建与回收,而对于布局等等问题并不关注。所以为了解决布局的问题,Android中使用布局管理器(LayoutManager)来设置RecyclerView的布局样式,LayoutManager是一个抽象类,Android中内置了几种它的实现类,分别呈现不同的布局效果。
LinearLayoutManager
线性的布局,有HORIZONTAL和VERTICAL两种,可以实现普通的ListView的效果与横向ListView的效果
GridLayoutManager
实现之前GridView的效果
StaggeredGridLayoutManager
实现酷炫的瀑布流的效果










也正是由于布局样式与Adapter之间是分开的,所以我们可以修改极小部分的代码就可以在ListView,GridView的样式中自由切换,还可以瞬间实现极为炫酷的瀑布流的效果。只需让我们的RecyclerView设置不同的LayoutManager就好了。
//        recycleView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));
recycleView.setLayoutManager(new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL));
//        recycleView.setLayoutManager(new GridLayoutManager(this,3));
//        recycleView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false));


5. 处理item的点击事件

在RecyclerView中并没有ListView中的OnItemClickListener和OnItemLongClickListener的接口,所以我们需要自己在Adapter中实现item的点击事件处理。虽然这样有一点麻烦,但也是有好处的,在ListView中,如果一个item中有一个Button时,那么如何处理这个Button的点击事件是非常麻烦的,而在RecyclerView中我们就可以为item中的任何一个控件设置独有的点击事件处理逻辑。这个点击事件的处理逻辑很显然应该在onBindViewHolder方法中进行实现。
//在这里我就对item中的图片设置了一个点击事件的处理,以及对整个item的Click处理和LongClick处理
@Override
public void onBindViewHolder(final DramaAdapter.DramaHolder holder, int position) {
Drama drama = mDramaList.get(position);
holder.icon.setImageResource(drama.getIconId());
holder.name.setText(drama.getName());
holder.progress.setText(drama.getProgress());
holder.icon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(v.getContext(),"你点击了第"+holder.getLayoutPosition()+"项的图片",Toast.LENGTH_SHORT).show();
}
});
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(v.getContext(),"你点击了第"+holder.getLayoutPosition()+"项",Toast.LENGTH_SHORT).show();
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
removeItem(holder.getLayoutPosition());
return true;
}
});
}


6. item的增加和删除

在Adapter中实现两个权限为public的方法供在Activity中调用
/**
* 添加子项的方法
* @param position
*/
public void addItem(int position){
Drama newOne = new Drama(R.mipmap.ic_launcher,"新的剧集","新的进度");
mDramaList.add(position,newOne);
notifyItemInserted(position);
}

/**
* 删除子项的方法
* @param position
*/
public void removeItem(int position){
if (position < mDramaList.size()){
mDramaList.remove(position);
notifyItemRemoved(position);
}
}

在这里需要重点说明的是最后的
notifyItemInserted(position);
notifyItemRemoved(position);
在执行添加item必须通过notifyItemInserted(position)来刷新视图
在执行删除item必须通过notifyItemRemoved(position)来刷新视图
而不是之前在ListView中使用notifyDataSetChanged()的方法。


在上述六个步骤完成后一个简单的RecyclerView的demo也就完成了,而对于item之间的分隔以及添加、删除item的动画效果等,在这里暂不做介绍,在我进一步学习之后会有新的手记来记录这方面的知识。关于item的分隔,一个简单的实现的方法是在自定义的item布局中添加整体的margin,而对于动画效果,有默认的样式,个人觉得效果还是不错的不做深入研究的话是已经可以满足需求了

作者: 啸宇 
链接:http://www.imooc.com/article/15474
来源:慕课网
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: