本篇文章你会了解以下内容:
1、抽取ViewHolder,实现优化第一步
2、抽取公共Adapter,告别冗余方法
3、控件直接赋值,让Adapter再次优化
ListView不得不说是Android开发当中出现率相当高的一个控件,什么资讯列表,联系人列表,消息列表等等,无不有它的身影存在。既然项目中有太多的地方会用到它,传统的代码逻辑我们是怎么使用的呢?每写一个ListView,都要去写一个Adapter类,一个ViewHolder类,这几乎是我们必须要操作的,以致于有太多太多的冗余代码,让我们感到真的不厌其烦,一个两个还可以,十个八个,就真的有点太崩溃了,不仅代码繁琐,还会占用内存,为了解决这样的一个问题,下面就要开始对其抽取优化,代码之间从上到下进行衔接,不要跳过看啊。
1、抽取ViewHolder,实现优化第一步
我们知道正常的ViewHolder,会在Adapter的getView方法里去操作很多事情,创建实例,赋值控件,setTag和getTag等,如下代码所示,是我们最常用的模式,这里我只用了两个控件,其实开发中有很多的View,对ViewHolder的优化,就是把这些代码封装进行抽取。
publicViewgetView(intposition, ViewconvertView, ViewGroupparent) { Viewview; ViewHolderviewHolder; if (convertView==null) { view=View.inflate(mContext, R.layout.item_view, null); viewHolder=newViewHolder(); viewHolder.tvTitle= (TextView) view.findViewById(R.id.tv_title); viewHolder.tvContent= (TextView) view.findViewById(R.id.tv_content); view.setTag(viewHolder); } else { view=convertView; viewHolder= (ViewHolder) view.getTag(); } viewHolder.tvTitle.setText(mDatas.get(position).getTitle()); viewHolder.tvContent.setText(mDatas.get(position).getContent()); returnview; } privatestaticclassViewHolder { TextViewtvTitle, tvContent; }
自定义一个ViewHolder类,实现构造方法,既然要实现对以上的代码进行一个抽取,那么getView里的参数,ViewHolder类大部分都需要用到,在ViewHolder类中,写一个静态方法,用于获取ViewHolder,其判断逻辑和以上代码思路一致,convertView若不存在就去创建ViewHolder,若存在就复用。
publicstaticViewHoldergetViewHolder(Contextcontext, intposition, ViewconvertView, ViewGroupparent, intlayoutId) { if (convertView==null) { returnnewViewHolder(context, position, parent, layoutId); } else { ViewHolderviewHolder= (ViewHolder) convertView.getTag(); viewHolder.mPosition=position; returnviewHolder; } }
实现其构造方法,用于convertView不存在时进行对其实例:
privateSparseArray<View>mView; privateintmPosition; privateViewmConvertView; publicViewHolder(Contextcontext, intposition, ViewGroupparent, intlayoutId) { mPosition=position; mView=newSparseArray<View>(); mConvertView=View.inflate(context, layoutId, null); mConvertView.setTag(this); }
mView是用于存储各个控件View,在这里为什么要用SparseArray而不用Map,我们可以去查看下源码,其实对于键为int类型,SparseArray要比map更加有效的使用内存。
mPosition是记录索引位置。
mConverView就是创建的View对象,注意看上述代码,它是在convertView为null的情况下,才去创建的。
写一个方法,用于通过ViewHolder来获取各个控件:
public<TextendsView>TgetView(intlayoutId) { Viewview=mView.get(layoutId); if (view==null) { view=getmConvertView().findViewById(layoutId); mView.put(layoutId, view); } return (T) view; }
再写一个获取layout的方法:
publicViewgetmConvertView() { returnmConvertView; }
经过以上代码,对于ViewHolder,我们基本上就抽取完成了,回过来,我们再看adapter里的getView方法(注意,原来adapter里的ViewHolder类可以对其删除),是不是一下整洁了许多。
publicViewgetView(intposition, ViewconvertView, ViewGroupparent) { ViewHolderviewHolder=ViewHolder.getViewHoder(mContext,position, convertView,parent,R.layout.item_view); ((TextView)viewHolder.getView(R.id.tv_title)). setText(mDatas.get(position).getTitle()); ((TextView)viewHolder.getView(R.id.tv_content)). setText(mDatas.get(position).getContent()); returnviewHolder.getmConvertView(); }
2、抽取公共Adapter,告别冗余方法
ViewHolder抽取完之后,省去了太多的代码,但是我们会发现Adapter里除了getView方法,其它三个也是重复的,那么接下来,我们就对Adapter进行抽取优化,新建一个万能的Adapter类,继承于BaseAdapter,记住要是抽象的,把getView方法设成抽象方法,这样其它Adapter继承这个万能Adapter,只需要重写getView方法即可。
publicabstractclassUniversalAdapter<T>extendsBaseAdapter { protectedList<T>mDatas; protectedContextcontext; privateintlayoutId; publicUniversalAdapter(Contextcontext, List<T>mDatas, intlayoutId) { this.mDatas=mDatas; this.context=context; this.layoutId=layoutId; } publicintgetCount() { returnmDatas.size(); } publicObjectgetItem(intposition) { returnmDatas.get(position); } publiclonggetItemId(intposition) { returnposition; } publicabstractViewgetView(intposition, ViewconvertView, ViewGroupparent); }
在上述代码中,细心的同志的可能看到了,实现其构造方法时,我传入了一个layoutId,其实就是一个XML资源,引入它,说白了,在getView方法里我连 ViewHolder viewHolder=ViewHolder.getViewHolder(mContext,position,convertView,parent,R.layout.item_view);这段代码我也不想去写,怎么办呢?很简单getView方法不去抽象化,而引出一个抽象的方法,具体可以改成以下方式:
publicViewgetView(intposition, ViewconvertView, ViewGroupparent) { ViewHolderviewHoder=ViewHolder.getViewHolder(context, position, convertView, parent, layoutId); convert(viewHolder, mDatas.get(position)); returnviewHolder.getmConvertView(); } protectedabstractvoidconvert(ViewHolderviewHolder, Objectitem);
以后我们所有的Adapter继承这个万能的Adapter之后,只需要重写convert这个方法就好了,Object item是一个Bean,获取时要进行强转。
publicclassMyAdapterextendsCourAdapter { publicMyAdapter(List<Bean>data, Contextcontext, intlayoutId) { super(context, data, layoutId); } protectedvoidconvert(ViewHoderviewHoder, Objectitem) { ((TextView)viewHolder.getView(R.id.tv_title)). setText(((Bean)item).getTitle()); ((TextView)viewHolder.getView(R.id.tv_content)). setText(((Bean)item).getContent()); } }
看看以上的代码,是不是就简单了很多呢。
3、控件直接赋值,让Adapter再次优化
经过以上的优化之后,代码大大的减少了,但是,我还想进行对其优化,也就是说,我连setText都不想去那么麻烦,这样的话,我们就是可以在ViewHolder这个类中去写控件赋值方法:
//TextView设置数据publicViewHoldersetText(intviewId, Stringtxt) { TextViewmTextView=getView(viewId); mTextView.setText(txt); returnthis; }
再看convert方法,是不是又一下简单的很多。
protectedvoidconvert(ViewHolderviewHolder, Objectitem) { viewHolder.setText(R.id.tv_title,((Bean)item).getTitle()) .setText(R.id.tv_content,((Bean)item).getContent()); }
当然了对于其它控件你也可以这样去做,比如ImageView:
publicViewHoldersetPic(intviewId, Stringurl) { ImageViewmImageView=getView(viewId); //ImageLoader.getInstance().loadImage(url,mImageView);returnthis; }