深入解析Android中的RecyclerView组件

深入解析Android中的RecyclerView组件,第1张

概述前些日子,组里为了在目前的Android程序里实现基于ListView子项的动画效果,希望将最新的RecyclerView引入到程序中,于是我便花了一些时间研究了一下RecyclerView的基本情况。本文算是对这些日子里了解的内容做一些汇

前些日子,组里为了在目前的AndroID程序里实现基于ListVIEw子项的动画效果,希望将最新的RecyclerVIEw引入到程序中,于是我便花了一些时间研究了一下RecyclerVIEw的基本情况。本文算是对这些日子里了解的内容做一些汇总。

在网上关于RecyclerVIEw的基本使用方式已经有了比较详细介绍,而且其设计结构也类似于ListVIEw,所以本文将不重点介绍如何使用,在文末的引用中都可以相关内容。这里主要是介绍RecyclerVIEw的基本功能、设计理念,以及系统提供API的情况。

@H_419_5@什么是RecyclerVIEw
RecyclerVIEw是在AndroID L(也就是后来的Lollipop)中新加入的一种VIEwGroup。但因为它使以support-v7库的形式加入到AndroID系统中,所以不仅仅是Lollipop版本以后的AndroID系统可以使用,只要系开发项目中引入这个库就在任意API级别中使用。不过查阅其文档,可以感受到RecyclerVIEw是如此的强烈的“还在完善中”的感觉,因为对它的介绍只有短短的一句话:

A flexible vIEw for provIDing a limited window into a large data set.

单看这句话,其实已经被经常使用的ListVIEw和GrIDVIEw基本也是满足这句描述的。而且从单呈现效果来看RecyclerVIEw和ListVIEw、GrIDVIEw并没有大多的差别。

不过它的“flexible”并不单单指它可以在ListVIEw和GrIDVIEw之间随意互相切换,更在于它可以创造出更多的复杂的可滚动的视图,比如水平方向的ListVIEw,或者是Web上很流行的瀑布流式布局(Masonry)。只是大部分布局系统都没有提供,必须由开发者自己实现。

所以RecyclerVIEw的“flexible”:什么都可以做,但什么都要自己做。

@H_419_5@RecyclerVIEw和ListVIEw的主要区别
只要用AndroID提供的LIEanerLayoutManager并配以VERTICAL模式,RecyclerVIEw就可以完美达到ListVIEw的基本效果。两者的设计结构也都是数据(Dataset)与视图(VIEw)分离,然后通过适配器(Adapter)来连接的方式。

但RecyclerVIEw相对ListVIEw来说有以下几大提升:

@H_419_5@强制使用VIEwHolder
在ListVIEw性能优化方面,AndroID就推荐使用VIEwHolder来减少findVIEwByID()的使用以提高效率。不过对于ListVIEw上的VIEwHolder,AndroID只是建议而非强制使用。不过因为使用VIEwHolder模式太有意义了,所以在RecyclerVIEw中VIEwHolder就变成了必须使用的模式,Adapter要求返回的也从普通的VIEw变成了VIEwHolder。不过如果实现时没有自定义的一些VIEw实际变量,VIEwHolder也依然失去其意义。

@H_419_5@没有OnItemClickListener
ListVIEw从它的父类AdapterVIEw直接继承了对子项目点击的响应,开发者可以定义自己的OnItemClickListner来接受点击事件。但这个设计也造成了一些问题,比如子项内部视图如果设置了OnClickListener,那么子项目视图本身并不会知道,从而可能会导致视图点击状态没有同步等问题。ReyclerVIEw没有提供简便的响应子项目被点击的监听器,虽然它有一个OnItemtouchListener,但在这个接口方法中没有任何关于那个子项目被点击的信息,该接口只是帮助开发者截获触摸事件,对于如何处理,检测被触摸目标对象都留给了开发者去完成

@H_419_5@视图与布局分离
ListVIEw做到了数据和视图的分离,RecyclerVIEw在视图和布局之间再进一步分离,于是便有了LayoutManager。RecyclerVIEw负责管理视图的重复利用,然后将布局方式全权交给了LayoutManager,通过配置或者切换LayoutManager就可以获得不同的布局效果。不像ListVIEw被限制在垂直滚动布局。同时RecyclerVIEw还提供了Itemdecoration,在已有的子视图基础上还可以添加额外的视图。比如做一条分割线,在ListVIEw就需要额外占用一个VIEwType来提供视图,现在则不需要在Adapter中加入这些与实际逻辑业务无关的辅助内容。

@H_419_5@支持子项目层次的动画效果
ListVIEw也可以支持子项目层次的动画效果,在AndroID Developers的DevBytes频道里有很多关于这方面的介绍,不过在看过其实现之后就会发现其解决方案是多么的丑陋而冗长。很多时候都是在计算和分析子视图的位置状态。RecyclerVIEw则带来了非常简洁的ItemAnimator接口。当Adapter中的数据发生“增删改移”变化,通过调用Adapter相应的方法就可以激活动画的产生。当然开发者还需要自己实现具体的ItemAnimator对象来完成所需的动画效果,但是其清晰的结构和接口已比ListVIEw有极大的进步。

@H_419_5@RecyclerVIEw的现状
上边提到RecyclerVIEw的套件已经加入了豪华Support library v7,而且是以单独的库放入,所以只要在AndroID Studio项目的Gradle编译文件的dependencIEs下加入下边的这句就可以开始使用RecyclerVIEw了:

compile 'com.androID.support:recyclervIEw-v7:21.0.+'
不过看过库中提供的那些自带对象实现,体现基本 *** 作流程,就会发现RecyclerVIEw能做的看起来很多,但是已经做到的实在太少。

@H_419_5@RecyclerVIEw.Adapter

RecyclerVIEw提供了一个抽象Adapter类,然后就没有了。没有任何可以直接使用的子类,像listadapter那样的ArrayAdapter、SimpleCursorAdapter现成的类都没有。一切都留给开发者自己去实现定义。

仔细想想这也很挺正常,相信应该很少在实际产品中有使用ArrayAdapter的,因为大部分列表都不会是简单的一行文字。对于CursorAdapter使用也往往会实现不同的继承类来提供子视图。再者RecyclerVIEw的Adapter和listadapter在理念上还是一样的,所以想实现个RecyclerVIEwCursorAdapter,直接从CursorAdapter取材便可。

RecyclerVIEw的Adapter相对listadapter在接口上有几处变化也值得注意。

首先其将getVIEw()方法分拆成了createVIEwHolder()和bindVIEwHolder()两个。不过这个没有什么好紧张的,在CursorAdapter里就已经有见到过这个更加合理的设计。另外返回对象也从VIEw变成了VIEwHolder只需提一下。

最关键的注意点在于createVIEwHolder(VIEwGroup parent,int vIEwType)第二参数虽然是整形,但是它并不是以往的当前子项的位置(position),而是调用getItemVIEwType()获得的的子项的类别。似乎在创建VIEwHolder时,RecyclerVIEw故意在隐藏子项的详细信息,希望开发者完全只依赖其类别来创建相应的VIEw以及VIEwHolder。

其次,RecyclerVIEw的Adapter除了和listadapter一样有notifyDataSetChanged()方法外,还有一堆会触发动画效果的通知数据改变的方法:

final voID notifyItemChanged(int position); final voID notifyItemInserted(int position); final voID notifyItemmoved(int fromposition,int toposition); final voID notifyItemRangeChanged(int positionStart,int itemCount); final voID notifyItemRangeInserted(int positionStart,int itemCount); final voID notifyItemRangeRemoved(int positionStart,int itemCount); final voID notifyItemRemoved(int position);

调用这些方法就会激发ItemAnimator上对应的用于产生动画的方法。

@H_419_5@RecyclerVIEw.VIEwHolder

在ListVIEw的年代里,其实已经在使用VIEwHolder,只是那时的方法看起来比较讨巧,要隐藏在VIEw的tag里。现在RecyclerVIEw强制使用VIEwHolder,并且VIEwHolder除了有对子视图的引用,还有诸如ItemVIEwType和position这些信息。

@H_419_5@RecyclerVIEw.LayoutManager

LayoutManager相对于ListVIEw来说是一个新部分,通过继承该类来实现自定义的布局方式,而不像ListVIEw只有固定的布局方式。Support库提供了两个现成的子类:linearlayoutmanager和StaggeredGrIDLayoutManager。前者可以获得和ListVIEw一样的布局,还可以是水平方向的;后者则提供了形如GrIDVIEw的布局。所以应用程序中的基本日常所以都可以被满足。

如果需要实现自定义的LayoutManager,就比较麻烦了,需要理解Recycle、Scrap、Dirty这些关于子项目视图状态的概念。本人还没有尝试过创建一个自定义的LayoutManager,但在文末的引用文档中有部分介绍实现方式的。

@H_419_5@RecyclerVIEw.ItemAnimator

通过实现继承实现ItemAnimator,然后创建对象设置到RecyclerVIEw上就可以得到基于子项目的动画效果。不过如何正确合理地创建一个ItemAnimator子类,却没有详细的描述指南。

窥探库中提供的唯一一个可用的子类DefaultItemAnimator,可以看出它的动画效果是简单的@R_403_5997@渐变。同时也会发现其实现是如此的复杂,有很多对于动画步骤的 *** 作,还得注意动画在中途被打断的处理,在结束时也要重置视图状态以便重用。这也反过来说明ItemAnimator基本没有提供任何关于如何实现和管理动画的信息。另一方面因为DefaultItemAnimator的实现过于具体,因此它并不是合适作为自定义ItemAnimator的父类。

相信当RecyclerVIEw越来越多的被应用到程序中时,更多关于这方面的合理设计会被提出来。目前在Github上也有不少参考了DefaultItemAnimator的实现方式,比如这个,还有这个。

关于ItemAnimator的使用,有几点值得提醒的是:如果没有提供ItemAnimator,RecyclerVIEw默认会创建一个DefaultItemAnimator用于动画,所以不需要显示地设置DefaultItemAnimator对象到RecyclerVIEw上;添加(add)和删除(remove)是默认起效的,但是修改(change)的效果需要调用setSupportsChangeAnimations(boolean)来指定是否启用,其默认是没有修改的动画。

总体而言,RecyclerVIEw的功能非常强大,其结构设计也十分开放,这也造成它的上手使用相对比较困难。随着越来越多的人开始尝试使用这个部件,也会有越来越深刻的理解和设计实现。另外,阅读RecyclerVIEw的源码可以帮助对其设计思想的了解,在以后设计其它的复用视图时可以有更好的参照。

@H_419_5@DEMO

说了这么多,我们来看一个RecyclerVIEw实现图片文字按钮的混排的demo:

首先还是看我的工程结构吧。

首先还是贴出我的main_acitivy.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:orIEntation="vertical" > <!-- A RecyclerVIEw with some commonly used attributes --><androID.support.v7.Widget.RecyclerVIEw androID:ID="@+ID/my_recycler_vIEw" androID:scrollbars="vertical" androID:layout_wIDth="match_parent" androID:layout_height="match_parent"/></linearLayout>

其他几个xml就不用贴了,很简单的放了写TextVIEw,imgeVIEw之类。

然后我们就来看看代码,首先是Bean里面的代码。

package com.androIDl.bob;public class Bean { public static final int Y_TYPE = 0; //vIEw类型0 public static final int X_TYPE = 1; //vIEw类型2 public static final int Z_TYPE = 2;//vIEw 类型3 private int type; private String text; public Bean(int type,String text) {  super();  this.type = type;  this.text = text; } public int getType() {  return type; } public voID setType(int type) {  this.type = type; } public String getText() {  return text; } public voID setText(String text) {  this.text = text; }}

然后是Adapter里面的代码:

package com.androIDl.bob;import java.util.List;import com.example.androIDl.R;import androID.support.v7.Widget.RecyclerVIEw;import androID.support.v7.Widget.RecyclerVIEw.VIEwHolder;import androID.vIEw.LayoutInflater;import androID.vIEw.VIEw;import androID.vIEw.VIEwGroup;import androID.Widget.button;import androID.Widget.Imagebutton;import androID.Widget.ImageVIEw;import androID.Widget.TextVIEw;import androID.Widget.Toast;/** * Date : 2014/7/15 *  * @author edsheng *  */public class RecycleAdapter extends RecyclerVIEw.Adapter<VIEwHolder> { private List<Bean> beans; public RecycleAdapter(List<Bean> beans) {  super();  this.beans = beans; } /**  * 内部TextHoler  *   * @author edsheng  *   */ public class TextHoler extends RecyclerVIEw.VIEwHolder {  public TextVIEw textVIEw;  public TextHoler(VIEw textvIEw) {   super(textvIEw);   this.textVIEw = (TextVIEw) textvIEw.findVIEwByID(R.ID.mytext);  } } /**  * iamgeHolder  *   * @author edsheng  *   */ public class ImageHoler extends RecyclerVIEw.VIEwHolder {  public ImageVIEw ImagevIEw;  public ImageHoler(VIEw textvIEw) {   super(textvIEw);   this.ImagevIEw = (ImageVIEw) textvIEw.findVIEwByID(R.ID.myiamge);  } } /**  * 按钮的holder  *   * @author edsheng  *   */ public class buttonHolder extends RecyclerVIEw.VIEwHolder {  public button button;  public buttonHolder(VIEw textvIEw) {   super(textvIEw);   this.button = (button) textvIEw.findVIEwByID(R.ID.mybutton);  } } @OverrIDe public int getItemCount() {  // Todo auto-generated method stub  return beans.size(); } /**  * 获取消息的类型  */ @OverrIDe public int getItemVIEwType(int position) {  // Todo auto-generated method stub  return beans.get(position).getType(); } /**  * 创建VIEwHolder  */ @OverrIDe public VIEwHolder onCreateVIEwHolder(VIEwGroup parent,int vIEwtype) {  // Todo auto-generated method stub  VIEw v = null;  VIEwHolder holer = null;  switch (vIEwtype) {  case Bean.X_TYPE:   v = LayoutInflater.from(parent.getContext()).inflate(     R.layout.recylce_item_x,null);   holer = new TextHoler(v);   break;  case Bean.Y_TYPE:   v = LayoutInflater.from(parent.getContext()).inflate(     R.layout.recylce_item_y,null);   holer = new buttonHolder(v);   break;  case Bean.Z_TYPE:   v = LayoutInflater.from(parent.getContext()).inflate(     R.layout.recylce_item_z,null);   holer = new ImageHoler(v);   break;  }  return holer; } /**  * 绑定vIEwholder  */ @OverrIDe public voID onBindVIEwHolder(VIEwHolder holder,int position) {  // Todo auto-generated method stub  switch (getItemVIEwType(position)) {  case Bean.X_TYPE:   TextHoler textholer = (TextHoler) holder;   textholer.textVIEw.setText(beans.get(position).getText());   break;  case Bean.Y_TYPE:   buttonHolder buttonHolder = (buttonHolder) holder;   buttonHolder.button.setText(beans.get(position).getText());   break;  case Bean.Z_TYPE:   ImageHoler imageHoler = (ImageHoler) holder;   // imageHoler.ImagevIEw.setimageResource(androID.R.drawable.checkBox_on_background);   break;  } }}

最后是activity的代码。

package com.androIDl.bob;import java.util.ArrayList;import java.util.List;import androID.app.Activity;import androID.os.Bundle;import androID.support.v7.Widget.linearlayoutmanager;import androID.support.v7.Widget.RecyclerVIEw;import com.example.androIDl.R;public class Mainactivity extends Activity {  @OverrIDe protected voID onCreate(Bundle savedInstanceState) {  // Todo auto-generated method stub  super.onCreate(savedInstanceState);  setContentVIEw(R.layout.main_activity);   RecyclerVIEw mRecyclerVIEw = (RecyclerVIEw) findVIEwByID(R.ID.my_recycler_vIEw);// // improve performance if you kNow that changes in content// // do not change the size of the RecyclerVIEw//  mRecyclerVIEw.setHasFixedSize(true);   //创建布局管理器  linearlayoutmanager mLayoutManager = new linearlayoutmanager(this);  mLayoutManager.setorIEntation(linearlayoutmanager.VERTICAL);  mRecyclerVIEw.setLayoutManager(mLayoutManager);  //初始化数据  List<Bean> myDataset = new ArrayList<Bean>();  myDataset.add(new Bean(Bean.Z_TYPE,"图片"));  myDataset.add(new Bean(Bean.X_TYPE,"文字"));  myDataset.add(new Bean(Bean.Y_TYPE,"按钮"));  myDataset.add(new Bean(Bean.Z_TYPE,"shit"));  myDataset.add(new Bean(Bean.X_TYPE,"我擦"));  myDataset.add(new Bean(Bean.Z_TYPE,"图片"));  myDataset.add(new Bean(Bean.Y_TYPE,"按钮"));  myDataset.add(new Bean(Bean.Y_TYPE,"按钮"));  myDataset.add(new Bean(Bean.X_TYPE,"文字"));  //创建Adapter  RecycleAdapter mAdapter = new RecycleAdapter(myDataset);  mRecyclerVIEw.setAdapter(mAdapter); }} 

总结

以上是内存溢出为你收集整理的深入解析Android中的RecyclerView组件全部内容,希望文章能够帮你解决深入解析Android中的RecyclerView组件所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1142081.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-31
下一篇2022-05-31

发表评论

登录后才能评论

评论列表(0条)

    保存