ListView复用和优化详解.docx
- 文档编号:17184849
- 上传时间:2023-04-24
- 格式:DOCX
- 页数:16
- 大小:524.47KB
ListView复用和优化详解.docx
《ListView复用和优化详解.docx》由会员分享,可在线阅读,更多相关《ListView复用和优化详解.docx(16页珍藏版)》请在冰豆网上搜索。
ListView复用和优化详解
ListView复用和优化详解
我们每一个Android开发人员对ListView的使用肯定是很熟悉的,然而多少人能真正的懂ListView的缓存机制呢,说白了就是ListView为了提高效率,而内部实现的一种优化,牺牲一点内存。
而这种优化就需要复用ItemView(也就是item对应的View).那么下面楼主来对ListView和RecyclerView的item复用问题做一个深入的讲解
先来一张大家学习的时候都遇到过的图
看不懂也没啥事,可以接着往下看,先有一个直观的认识
首先来解答几个问题
1.ListView为什么会存在Item复用问题
答:
ListView内部为了优化而建立的复用机制,在下面方法中第二个参数就是ListView传递给你,让你进行复用的View.如果你不想复用listview传递给你的View,那你每次都需要创建一个新的View进行返回,这样子是肯定不会出现复用问题的,但是性能却是很消耗的。
publicViewgetView(intposion,ViewitemView,ViewGroupviewGroup)
{
returnnull;
}
2.为什么上述的getView方法中第二个参数有时候为null呢
因为ListView默认缓存一页的View,什么叫一页,也就是你当前listview界面上有几个Item可以显示,listview就缓存几个.
当现实第一页的时候,由于没有一个Item被创建,所以第一页的Item的getView方法中的第二个参数都是为null的
假如listview只能最多显示8条记录,则第一页显示的时候listview内部缓存了这8个itemView.当第九条记录出现在视野中的时候,listview就会在调用getView方法的时候在第二个参数处传入之前用过的itemView。
3.为什么需要ViewHolder呢?
这个又是干嘛的
为什么需要上述我们谈到itemView的复用是为了性能,那么ViewHolder同样也是为了提高性能.我们都知道我们要显示列表数据.就要在getView方法中拿到对应下标的数据然后对itemView中的控件进行设值,所以我们需要用到findViewById(intid)方法来找到控件,并且强转成我们想要的类型之后,然后设置数据,而findViewById(intid)方法在列表滚动的时候频繁调用getView方法的时候也是一个比较消耗性能的操作.所以ViewHolder来了
ViewHolder是干嘛的为了在列表滚动的时候,频繁调用getView方法的时候尽量提高性能.我们可以使用一个普通类,这个类通常就起名字为ViewHolder了,当创建itemView的时候,我们也把里面要用到的控件也找到,然后放在ViewHolder类中,然后再通过itemView.setTag(Objectob)方法实现一个itemView和一个ViewHolder进行绑定.
经过上述的操作,如果在getView方法中传入了复用的itemView,那么我们可以毫不客气地从里面拿出这个itemView对应的ViewHolder,从而避免了去调用多个findViewById(intid)去找到控件并设值.因为之前你把找到的控件都放在了ViewHolder中
扩展如果你的itemView中只有一个控件需要显示,那么ViewHolder就不需要了,你可以直接把这个控件和itemView进行关联,也就是你需要深刻理解ViewHolder的作用,它是为了把你找到的多个控件和itemView关联。
所以当你只有一个控件的时候,这个ViewHolder就不需要啦
itemView.setTag(Objectob)方法直接把这个控件设置上去就可以啦,复用的时候直接拿出来
那么主要的问题解答完了,总得写点代码来让大家更深刻的体会一下.
博主几乎会重现我们开发中的常见问题,来对应的讲解
getView方法在什么时候调用
回答:
在每一个item从不可见变为可见的时候
动手实践
实现一个简单的列表,使用ListView控件,并且Item中有复选框
Activity的xml文件
xmlversion="1.0"encoding="utf-8"?
>
android=" xmlns: tools=" android: layout_width="match_parent" android: layout_height="match_parent" tools: context="com.xiaojinzi.listdemo.MainActivity"> android: id="@+id/lv" android: layout_width="match_parent" android: layout_height="match_parent"/> 就是一个列表控件 ListView的Item的xml xmlversion="1.0"encoding="utf-8"? > android=" android: layout_width="match_parent" android: layout_height="wrap_content" android: gravity="center_vertical" android: padding="4dp" android: orientation="horizontal"> android: id="@+id/tv" android: layout_width="wrap_content" android: layout_height="wrap_content" android: layout_marginLeft="30dp" android: textSize="18sp" android: text="hello"/> android: id="@+id/cb" android: layout_width="wrap_content" android: layout_height="wrap_content" android: layout_marginLeft="30dp"/> ListView的适配器 publicclassListViewAdapterextendsBaseAdapter{ privateList privateContextmContext; publicListViewAdapter(List this.listViewData=listViewData; this.mContext=mContext; } @Override publicintgetCount(){ returnlistViewData.size(); } @Override publicObjectgetItem(inti){ returnlistViewData.get(i); } @Override publiclonggetItemId(inti){ returni; } @Override publicViewgetView(inti,Viewview,ViewGroupviewGroup){ Viewitem=View.inflate(mContext,R.layout.listview_item,null); returnitem; } } 这代码非常简单,不再啰嗦 Activity代码 publicclassMainActivityextendsAppCompatActivity{ privateListViewlv; privateBaseAdapterlistViewAdapter; privateList @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(edInstanceState); setContentView(R.layout.activity_main); for(inti=0;i<50;i++){ listViewData.add("text"+i); } lv=(ListView)findViewById(R.id.lv); listViewAdapter=newListViewAdapter(listViewData,this); lv.setAdapter(listViewAdapter); } } 代码贴完了,都是非常的简单,先看下运行效果 这里很需要你们关注的是我们的适配器中的getView中的代码 publicViewgetView(inti,Viewview,ViewGroupviewGroup){ Viewitem=View.inflate(mContext,R.layout.listview_item,null); returnitem; } 我们上面说过了方法中第二个参数是ListView会传的itemView,提高效率用的,而这里博主先不用,每次调用getView都会创建一个新的View然后返回 实现一个小目标,嗯: 奇数的Item中的复选框要被选中 那么很容易,只需要这样子 publicViewgetView(intposition,Viewview,ViewGroupviewGroup){ Viewitem=View.inflate(mContext,R.layout.listview_item,null); //找到文本框 TextViewtv=(TextView)item.findViewById(R.id.tv); //设置文本内容 tv.setText(listViewData.get(position)); //找到复选框 CheckBoxcb=(CheckBox)item.findViewById(R.id.cb); if(position%2! =0){//如果是奇数 cb.setChecked(true); } returnitem; } 代码也很简单,就是找到了创建的布局item中的文本控件和复选框,然后设置相应的内容 看效果 我们可以看到,功能实现了,而且没有出现任何问题,比如常见的复用问题,嗯 喂喂喂,我们没复用回传的View,哪里来的复用问题啊,哈哈哈,所以我们的列表是肯定没有任何问题的,因为根本没有复用,性能是最差的一种写法 实现一个小目标,复用Item,嗯 publicViewgetView(intposition,Viewview,ViewGroupviewGroup){ Viewitem=null; if(view==null){ item=View.inflate(mContext,R.layout.listview_item,null); }else{ item=view; } //找到文本框 TextViewtv=(TextView)item.findViewById(R.id.tv); //设置文本内容 tv.setText(listViewData.get(position)); //找到复选框 CheckBoxcb=(CheckBox)item.findViewById(R.id.cb); if(position%2==0){//如果是奇数 cb.setChecked(true); } returnitem; } 这段代码改动的地方就是方法最开始,判断了一下回传给我的view是不是为null,为null的情况博文最开始已经讲过了 如果为null就创建一个新的,如果不是就直接赋值给item,达到条目的复用! 那我们看看效果呗! 请大声的告诉我,发生了什么? 复用问题 没错,复用问题出现了,博主给大家重现了错误 那么这里是怎么引起的呢? 只有知道其中的原理,你解决问题才能快准狠! 首先我先帮大家统计一下创建Item的次数 从App运行到滑动来滑动去,我们可以看见,最开始创建了16次,然后随着滑动多来了一次,你可以使用截图定格一下动图,你会发现这个列表最多显示17条记录(当然了你的界面是多少个和我这个界面是不同的,反正就是界面能显示的Item最多个数),所以证明了上面的一个观点,ListView默认缓存一个界面的Item个数 原理 所以当我们复用ListView回传的View的时候,这个View是被之前使用过的,也就是说给你的这个View保存了之前用过的状态 这里的情况就是给你的view刚好是之前复选框被选中的那个View,所以就造成复用啦 解决方法 对产生问题的控件进行初始化,初始化时什么意思呢? 意思就是说,把出问题的控件,状态还原一下 看代码! 别看了,就是框框里面的一句话,是不是感觉很简单呀,如果你知晓原理,为什么这样子就没有了复用的问题呢? 因为如果给你的View里面的复选框是被选中的,这里你对他还原了呀,所以就ok啦 使用ViewHolder 上面我们也说了ViewHolder的作用和使用的必要性,那么博主直接来用一下吧 由于getView内部稍微改动有点大,我贴上Adapter中的代码 publicclassListViewAdapterextendsBaseAdapter{ privateList privateContextmContext; publicListViewAdapter(List this.listViewData=listViewData; this.mContext=mContext; } @Override publicintgetCount(){ returnlistViewData.size(); } @Override publicObjectgetItem(inti){ returnlistViewData.get(i); } @Override publiclonggetItemId(inti){ returni; } @Override publicViewgetView(intposition,Viewview,ViewGroupviewGroup){ //Item对应的试图 Viewitem=null; ViewHoldervh=null; if(view==null){ item=View.inflate(mContext,R.layout.listview_item,null); vh=newViewHolder(); //找到文本框 vh.tv=(TextView)item.findViewById(R.id.tv); //找到复选框 vh.cb=(CheckBox)item.findViewById(R.id.cb); //让item和ViewHolder绑定在一起 item.setTag(vh); }else{ //复用ListView给的View item=view; //拿出ViewHolder vh=(ViewHolder)item.getTag(); } //设置文本内容 vh.tv.setText(listViewData.get(position)); //还原状态 vh.cb.setChecked(false); if(position%2==0){//如果是奇数 vh.cb.setChecked(true); } returnitem; } /** *用于存放一个ItemView中的控件,由于这里只有两个控件,那么声明两个控件即可 */ classViewHolder{ TextViewtv; CheckBoxcb; } } 1.如果复用的View为null,我们需要创建一个新的item,同时也创建了一个ViewHolder,然后把条目视图中的控件通过findViewById方法寻找到 ViewHolder中,然后我们说了需要和条目视图进行绑定,所以调用了setTag方法 2.而另一边,如果复用的View不是为null,那么直接拿过来用,并且从里面拿出ViewHolder,因为每一个复用的ViewHolder肯定是经过1处创建并且返回的
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ListView 优化 详解