首先,雖然大家都知道,還是提一下,利用好 convertView 來重用 View,切忌每次 getView() 都建立。ListView 的核心原理就是重用 View。ListView 中有一個回收器,Item 滑出界面的時候 View 會回收到這裡,需要顯示新的 Item 的時候,就盡量重用回收器裡面的 View。
利用好 View Type,例如你的 ListView 中有幾個類型的 Item,需要給每個類型建立不同的 View,這樣有利于 ListView 的回收,當然類型不能太多;
盡量讓 ItemView 的 Layout 層次結構簡單,這是所有 Layout 都必須遵循的;
善用自定義 View,自定義 View 可以有效的減小 Layout 的層級,而且對繪制過程可以很好的控制;
盡量能保證 Adapter 的 hasStableIds() 傳回 true,這樣在 notifyDataSetChanged() 的時候,如果 id 不變,ListView 将不會重新繪制這個 View,達到優化的目的;
使用 RecycleView 代替。 ListView 每次更新資料都要 notifyDataSetChanged(),有些太暴力了。RecycleView 在性能和可定制性上都有很大的改善,推薦使用。
比如你的Item中有三個按鈕,你要為三個按鈕分别定義點選事件,如何定義?
也許你會在getView中這樣做
如果你每屏顯示7個Item,你一共建立了21個listener對象在記憶體中,如果View回收不暢,會更多,這樣,在滾動的時候頻繁GC 就會導緻卡頓(這裡描述有誤,請看7月25日更新)
如果你在Adapter初始化的時候建立一個Listener
通過傳入的View v這個參數判斷是哪一個button被點選,這樣,無論View如何建立,你隻建立了1個Listener對象
這隻是一個小細節,優化的方式要綜合使用,才會事半功倍
------------------7月24日更-----------------
v.getTag() 這個tag本不是用來存資料的,通俗點講它和view 的Id是同一個東西,隻不過tag的類型是Object。實際上在tag中存儲資料是不符合規範的方式
但其實View類有兩種tag,
這是一個私有方法
他是用 SparseArray 實作的,我們可以把需要的東西存到這裡面(其實觀察源碼可以發現,系統很多時候都是這樣做的)
這裡要注意一點,參數key必須是唯一的,那麼我們可以這樣做
那麼需要先在res/values/strings.xml中添加
使用的時候寫成
-------------------------關于如何綁定資料的問題-----------------
有人問,如果這樣寫,所有button隻能通過id區分邏輯,無法傳入每個item的資料
我們可以将資料通過view 的tag帶進來
然後在listener中通過v.getTag()将資料取出
-----------------------7月25日update-------------------
這裡我有一個失誤
如果listener裡面的邏輯與目前的item有關,那麼其實并不隻是建立了21個listener對象
你看下,如果綁定資料在convertView為空的情況下确實隻建立了有限個listener,
但是在這種情況下綁定上的資料隻有View建立時的7個,之後不為空的情況下沒有更換listener導緻重用的view資料是新的,listener裡面的position依然是過去建立view時的7個之一,不會變化(注意參數上的final)
若在else裡面再重新setListener,view是有重用,listener被換成新的,并與新的position綁定,老的listener就會變成一個廢對象,等待gc回收,随着list滾動,越來越多
關鍵是我們的業務與position這個參數有關
1.重用 convertView
用以避免重複建立 View,重複建立 View 代價較大,而且如果重用 view 不改變寬高,重用View可以減少重新配置設定緩存造成的記憶體頻繁配置設定/回收;
2. 避免在 getView 中有 重複調用的 findViewById
findViewById 的實作是周遊,如果你定義的 View 越複雜代價越大。
Google 推薦的做法是用 ViewHolder,然後儲存在 view 的 tag 中。現在 RecyclerView 也是強制使用 ViewHolder 了。
3. 設定 View (如 TextView#setText )之前先對比資料是否有改變
一般來說,【比較兩個資料的代價】遠小于【 View 的重繪的代價】
4. 避免在 getView 函數中直接加載 Image 或做其他比較耗時的操作
加載本地 Image 需要載入記憶體以及解析 Bitmap ,都是比較耗時的操作。
使用者快速滑動清單時,會大量調用 getView ,而 getView 是在主線程中被調用的。如果你在 getView 函數中直接加載 Image 或做其他耗時操作,就會造成滑動比較卡。加載 ImageView 的解決方案就是開一個線程去把做這事。有很多第三庫可以做這事。
5. ListView 中元素避免半透明
半透明繪制需要大量乘法計算,在滑動時不停重繪會造成大量的計算,在比較差的機子上會比較卡。在設計上能不半透明就不不半透明。實在要弄的話我個人是用個比較偷懶的方法,是在滑動的時候把半透明設定成不透明,滑動完再重新設定成半透明。
6. 盡量開啟硬體加速
硬體加速提升巨大,避免使用一些不支援的函數導緻含淚關閉某個地方的硬體加速。
當然這一條不隻是對 ListView。
7.用 ListView 威力加強版 -- RecyclerView
更多的新武将,更多的姿勢,更規範的使用,更好用的動畫,更加強大的變化
轉載自知乎(http://www.zhihu.com/question/19703384)