在Android程序中,列表是一个很重要的部分,在看法中我们需要关心ListView的各方面的效率。最近也一直在思考这些问题,刚好看到Facebook的ListView优化,所以借鉴Facebook的方案将自己项目中的部分模块优化。请参考Fast Rendering News Feed on Android 、facebook新闻页ListView优化 。
1.基本知识 1.1 首先使用ViewHolder和ItemViewType优化ListView。 1.2 合理的任务调度。 1.3 降低ItemView的复杂度(嵌套层次)
2.Facebook优化方案。 使用基本的优化方案可以保证简单的ListView保持流畅,但是对于ListView中Item类型过多,基本的优化方案会导致ViewItem复用率低。
2.1 将每个Item拆分为多个Item。 将Item上部分和下部分相同的模块提取出来作为一个Item,即每一个Item分成多个Item,使得多个Item可以达到复用,提高复用率。
数据处理也需要将每个Item拆分为多个Item
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static FeedWrap feed2ItemTop (Feed feed) { if (feed == null ) return null ; return new FeedWrap(FeedItem.HEAD, feed.getFeedId(), new FeedHead(feed.getSenderName(), feed.getFeedTime(), feed.getSenderImg())); } public static FeedWrap feed2ItemButtom (Feed feed) { if (feed == null ) return null ; return new FeedWrap(FeedItem.BUTTOM, feed.getFeedId(), new FeedButtom(feed.getIsPraiseByMe(), feed.getRelpyNum(), feed.getForwardNum(), feed.getPraiseNum())); } public static FeedWrap feed2ConTent (Feed feed) {} public static List<FeedWrap> feed2ConTent (List<Feed> feeds) { if (feeds == null ) return null ; ArrayList<FeedWrap> feedWraps = new ArrayList<>(); for (Feed feed : feeds) { feedWraps.add(feed2ItemTop(feed)); feedWraps.add(feed2ConTent(feed)); feedWraps.add(feed2ItemButtom(feed)); } return feedWraps; }
2.2 预处理数据 数据格式化,创建spannable。
比如Json数据的反序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static FeedWrap feed2Wrap (Feed feed) { if (feed == null ) return null ; FeedContent feedContent = null ; switch (feed.getFeedType()) { case TEXT: feedContent = GsonUtil.getGson().fromJson(feed.getFeedContent(), FeedContentText.class ) ; return new FeedWrap(FeedItem.Content.TEXT, feed.getFeedId(), feedContent); case IMAGE: feedContent = GsonUtil.getGson().fromJson(feed.getFeedContent(), FeedContentImage.class ) ; return new FeedWrap(FeedItem.Content.IMAGE, feed.getFeedId(), feedContent); case VIDEO: feedContent = GsonUtil.getGson().fromJson(feed.getFeedContent(), FeedContentVideo.class ) ; return new FeedWrap(FeedItem.Content.VIDEO, feed.getFeedId(), feedContent); case REPOST: feedContent = GsonUtil.getGson().fromJson(feed.getFeedContent(), FeedContentRepost.class ) ; return new FeedWrap(FeedItem.Content.REPOST, feed.getFeedId(), feedContent); case NEWS: feedContent = GsonUtil.getGson().fromJson(feed.getFeedContent(), FeedContentNews.class ) ; return new FeedWrap(FeedItem.Content.NEWS, feed.getFeedId(), feedContent); default : return new FeedWrap(FeedItem.Content.UNKNOW, feed.getFeedId(), null ); } }
2.3 显示数据 FaceBook的方案是将没个Item做成一个自定义的View,方便管理,在demo中为了节省时间省略。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 private View getViewByViewType (int itemViewType, ViewGroup viewGroup) { View view = null ; Holder holder = null ; switch (itemViewType) { case FeedItem.HEAD: view = mInflater.inflate(R.layout.feed_item_head, viewGroup, false ); holder = new HeadViewHolder(view); break ; case FeedItem.BUTTOM: view = mInflater.inflate(R.layout.feed_item_bottom, viewGroup, false ); holder = new ButtomViewHolder(view); break ; case FeedItem.Content.TEXT: view = mInflater.inflate(R.layout.feed_item_text, viewGroup, false ); holder = new TextViewHolder(view); break ; case FeedItem.Content.VIDEO: view = mInflater.inflate(R.layout.feed_item_video, viewGroup, false ); holder = new VideoViewHolder(view); break ; default : view = mInflater.inflate(R.layout.feed_item_unknow, viewGroup, false ); break ; } view.setTag(holder); return view; }
2.4 操作数据 之前数据分为多个Item和显示数据较为简单, 但是在操作数据中就会遇到问题,我分为了两种方案。第一种方案在删除数据时非常安全,但是每次删除会遍历集合,效率方面存在问题;第二种方案根据自己的实际情况,直接通过position删除数据,但这种删除数据给人一种存在隐患。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public void remove (FeedWrap feedWrap) { if (feedWrap!= null && mFeedWraps != null ) { Iterator<FeedWrap> feedWrapIterator = mFeedWraps.iterator(); while (feedWrapIterator.hasNext()) { FeedWrap tempFeedWrap = feedWrapIterator.next(); if (tempFeedWrap.getFeedId() == feedWrap.getFeedId()) { feedWrapIterator.remove(); } } } } public boolean remove (FeedWrap feedWrap, int position) { if (checkRemoveData(feedWrap, position)) { mFeedWraps.remove(position); mFeedWraps.remove(position); mFeedWraps.remove(position); return true ; } return false ; }
在Item点击的时候,默认ListView的Item有selector效果,但是分为多个Item以后,selector效果会导致Item分块, 看起来不是一个整体, 可以在ListView中设置属性selector为透明。
1 android:listSelector="#00000000"
3. 对Facebook方案疑惑的地方。 因为对Facebook方案存在疑惑,所以我没有使用Binder和PartDefinition。 3.1 It is called before the first time a binder is bound, and intelligently scheduled when there’s free CPU time on the UI thread.This makes it ideal for allocating click listeners, formatting strings, building spannables and so forth。 在Binder的prepare中进行click事件绑定,而prepare是在cpu闲时主线程中调用的(那就肯定不是getView中调用了),那就没有View, 我也不知道他是怎么绑定click事件的。所以我感觉prepare只是做一些数据的预处理操作。click应该在bind中设置。
Demo源码