一起撸个朋友圈吧(step5) - 控件篇【评论控件优化】
項目地址:github.com/razerdp/Fri…
上篇鏈接:http://www.jianshu.com/p/4cc3f9c8a713
下篇鏈接:http://www.jianshu.com/p/885538a261ea
在昨天的那篇文章,我們的評論控件的優化里提及過希望采用一個對象池來保存,碰巧,今天在看ViewGroup的代碼的時候,發現了這么一個接口:
根據文檔,可以知道這個接口在ViewGroup層次改變的時候會回調,回調兩個方法:onChildViewAdded和onChildViewRemoved,這兩個方法正是與addView和removeView掛鉤。
在昨天我們提出過希望在removeView的時候回收,下次再add的時候從池里面拿,在今天看到這個接口后,在下就覺得-----是時候了。。。
首先我們構造一個簡單的池,用數組包著view就可以了
static class CommentPool{private CommentWidget[] CommentPool;private int size;private int curPointer=-1;public CommentPool(int size) {this.size = size;CommentPool=new CommentWidget[size];}public synchronized CommentWidget get(){if (curPointer==-1||curPointer>CommentPool.length)return null;CommentWidget commentTextView=CommentPool[curPointer];CommentPool[curPointer]=null;curPointer--;return commentTextView;}public synchronized boolean put(CommentWidget commentTextView){if (curPointer==-1||curPointer<CommentPool.length-1) {curPointer++;CommentPool[curPointer] = commentTextView;return true;}return false;}public void clearPool(){for (int i = 0; i < CommentPool.length; i++) {CommentPool[i]=null;}curPointer=-1;}} 復制代碼我們主要實現三個方法:
- put:入池(不是入棧哦)
- get:出池
- clear:清池
因為java木有指針,所以我們采用自己夠早一個游標的方法來進行池對象的推入和獲取。
在昨天的代碼里,我們構造一個靜態池,防止每次反射創建時被初始化。
//評論區的view對象池 private static final CommentPool COMMENT_TEXT_POOL=new CommentPool(20); 復制代碼存入的數量大概20條就足夠了,接下來實現OnHierarchyChangeListener接口,然后改造我們的addCommentWidget方法:
private void addCommentWidget(List<CommentInfo> commentList) {if (commentList == null || commentList.size() == 0) return;/*** 優化方案:* 因為是在listview里面,那么復用肯定有,意味著滑動的時候必須要removeView或者addView* 但為了性能提高,不可以直接removeAllViews* 于是采取以下方案:* 根據現有的view進行remove/add差額* 然后統一設置** 2016-02-26:復用池進一步優化* */final int childCount = commentLayout.getChildCount();commentLayout.setOnHierarchyChangeListener(this);if (childCount < commentList.size()) {//當前的view少于list的長度,則補充相差的viewint subCount = commentList.size() - childCount;for (int i = 0; i < subCount; i++) {CommentWidget commentWidget =COMMENT_TEXT_POOL.get();if (commentWidget == null) {commentWidget = new CommentWidget(context);LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);params.topMargin = 1;params.bottomMargin = 1;commentWidget.setLayoutParams(params);commentWidget.setLineSpacing(4, 1);}commentWidget.setOnClickListener(this);commentWidget.setOnLongClickListener(this);commentLayout.addView(commentWidget);}}else if (childCount > commentList.size()) {//當前的view的數目比list的長度大,則減去對應的viewcommentLayout.removeViews(commentList.size(), childCount - commentList.size());}//綁定數據for (int n = 0; n < commentList.size(); n++) {CommentWidget commentWidget = (CommentWidget) commentLayout.getChildAt(n);if (commentWidget != null) commentWidget.setCommentText(commentList.get(n));}} 復制代碼代碼更改量不大,我這里的邏輯是這樣的:
- 首先從池里面拿出可復用的對象
- 如果沒有,則創建一個對象
- 如果有,則直接使用這個對象
- 結合昨天的邏輯,判斷當前view的數量與評論數量,不足,則用第一步的對象綁定數據,超過,則removeView
- 在removeView的時候,對象進池待復用。
這樣,我們就可以不需要每次都new出那么多對象了,在onChildViewRemoved的代碼我們這么寫:
public void onChildViewRemoved(View parent, View child) {if (child instanceof CommentWidget)COMMENT_TEXT_POOL.put((CommentWidget)child);} 復制代碼也許會有一個問題:
- view被remove后入池,但之后復用這個對象,那么與上一個viewholder會不會沖突
這里我也想過,但有一個地方需要注意,我們在滑動的時候,viewholder不同的話,對應的實體類也是不同的,而我們每次都會重新綁定數據,因此我們的對象是相同,但我們操作的數據是不同的,所以并不影響。
最后在put和get打上log看看我們有沒有成功拿到數據:
app效果圖如下:
可以看得出,我們快速滑動的時候并沒有出現明顯的卡頓,為了更方便測試,有些地方的評論都快10條了(gif錄制的時候我設置了延時0.1s)然后我們監測我們的Log...
可以看得出,在remove的時候差額view都成功入池,而取出的時候都可以拿到,實際上new出來的對象確實不多 下面的圖是對new對象的監測
可以看到,在池空的時候創建了對象,池不空的時候創建次數就少了很多,這在一定程度上是有效果的。
下一步將會進行內容頁的填充。
總結
以上是生活随笔為你收集整理的一起撸个朋友圈吧(step5) - 控件篇【评论控件优化】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 5.1 vim—5.4vim编辑器
- 下一篇: Robotframework集成jenk