日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

Listview性能优化

發(fā)布時(shí)間:2025/7/14 57 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Listview性能优化 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
  • 首先,雖然大家都知道,還是提一下,利用好 convertView 來重用 View,切忌每次 getView() 都新建。ListView 的核心原理就是重用 View。ListView 中有一個(gè)回收器,Item 滑出界面的時(shí)候 View 會(huì)回收到這里,需要顯示新的 Item 的時(shí)候,就盡量重用回收器里面的 View。
  • 利用好 View Type,例如你的 ListView 中有幾個(gè)類型的 Item,需要給每個(gè)類型創(chuàng)建不同的 View,這樣有利于 ListView 的回收,當(dāng)然類型不能太多;
  • 盡量讓 ItemView 的 Layout 層次結(jié)構(gòu)簡單,這是所有 Layout 都必須遵循的;
  • 善用自定義 View,自定義 View 可以有效的減小 Layout 的層級(jí),而且對(duì)繪制過程可以很好的控制;
  • 盡量能保證 Adapter 的 hasStableIds() 返回 true,這樣在 notifyDataSetChanged() 的時(shí)候,如果 id 不變,ListView 將不會(huì)重新繪制這個(gè) View,達(dá)到優(yōu)化的目的;
  • 每個(gè) Item 不能太高,特別是不要超過屏幕的高度,可以參考 Facebook 的優(yōu)化方法,把特別復(fù)雜的 Item 分解成若干小的 Item,特別推薦看一下這個(gè)文章:code.facebook.com/posts
  • 為了保證 ListView 滑動(dòng)的流暢性,getView() 中要做盡量少的事情,不要有耗時(shí)的操作。特別是滑動(dòng)的時(shí)候不要加載圖片,停下來再加載,這個(gè)庫可以幫助你 Glide:github.com/bumptech/gli
  • 使用 RecycleView 代替。 ListView 每次更新數(shù)據(jù)都要 notifyDataSetChanged(),有些太暴力了。RecycleView 在性能和可定制性上都有很大的改善,推薦使用。
    ?
  • ? 比如你的Item中有三個(gè)按鈕,你要為三個(gè)按鈕分別定義點(diǎn)擊事件,如何定義?

    也許你會(huì)在getView中這樣做
  • 1 button1.setOnclickListener(new View.OnClickListener() { 2 @override 3 public void onClick(View v) { 4 //balabalabala... 5 } 6 }); 7 button2.setOnclickListener(new View.OnClickListener() { 8 @override 9 public void onClick(View v) { 10 //balabalabala... 11 } 12 }); 13 button3.setOnclickListener(new View.OnClickListener() { 14 @override 15 public void onClick(View v) { 16 //balabalabala... 17 } 18 });

    ?

  • 如果你每屏顯示7個(gè)Item,你一共創(chuàng)建了21個(gè)listener對(duì)象在內(nèi)存中,如果View回收不暢,會(huì)更多,這樣,在滾動(dòng)的時(shí)候頻繁GC 就會(huì)導(dǎo)致卡頓(這里描述有誤,請(qǐng)看7月25日更新)

    如果你在Adapter初始化的時(shí)候創(chuàng)建一個(gè)Listener public MyAdapter () {myListener = new View.OnClickListener() {@overridepublic void onClick(View v) {v.getTag()v.getId()//balabalabala... }}); }

    ?

    通過傳入的View v這個(gè)參數(shù)判斷是哪一個(gè)button被點(diǎn)擊,這樣,無論View如何創(chuàng)建,你只創(chuàng)建了1個(gè)Listener對(duì)象

    這只是一個(gè)小細(xì)節(jié),優(yōu)化的方式要綜合使用,才會(huì)事半功倍


    ------------------7月24日更-----------------

    v.getTag() 這個(gè)tag本不是用來存數(shù)據(jù)的,通俗點(diǎn)講它和view 的Id是同一個(gè)東西,只不過tag的類型是Object。實(shí)際上在tag中存儲(chǔ)數(shù)據(jù)是不符合規(guī)范的方式

    但其實(shí)View類有兩種tag,
    setTag(Object?tag) 方法將tag保存在一個(gè)成員變量中,findViewWithTag正是遍歷此tag

    setTag(int key,?Object?tag) 方法是最終是調(diào)用View類中的setKeyedTag(int key, Object tag)
    這是一個(gè)私有方法
    他是用 SparseArray 實(shí)現(xiàn)的,我們可以把需要的東西存到這里面(其實(shí)觀察源碼可以發(fā)現(xiàn),系統(tǒng)很多時(shí)候都是這樣做的)

    這里要注意一點(diǎn),參數(shù)key必須是唯一的,那么我們可以這樣做
    那么需要先在res/values/strings.xml中添加
  • <resources> <item type="id" name="tag_first"></item> <item type="id" name="tag_second"></item> </resources>

    ?

  • 使用的時(shí)候?qū)懗?/span>
  • view.setTag(R.id.tag_first, obj1); view.setTag(R.id.tag_second, obj2);

    ?

  • -------------------------關(guān)于如何綁定數(shù)據(jù)的問題-----------------

    有人問,如果這樣寫,所有button只能通過id區(qū)分邏輯,無法傳入每個(gè)item的數(shù)據(jù)

    我們可以將數(shù)據(jù)通過view 的tag帶進(jìn)來
    1 public View getView(.....) { 2 .... 3 v.setTag(key, getItem(position)); 4 .... 5 } 然后在listener中通過v.getTag()將數(shù)據(jù)取出
    -----------------------7月25日update-------------------
    這里我有一個(gè)失誤
    如果listener里面的邏輯與當(dāng)前的item有關(guān),那么其實(shí)并不只是創(chuàng)建了21個(gè)listener對(duì)象
    1 public void getView (View convertView ,final int position ....) { 2 if (convertView == null) { 3 View v = LayoutInflater.from(mContext).inflate(...); 4 v.setOnclickListener(new View.OnClickListener () { 5 @override 6 public void onClick(View v) { 7 getItem(position); 8 } 9 }); 10 } else { 11 12 } 13 } 你看下,如果綁定數(shù)據(jù)在convertView為空的情況下確實(shí)只創(chuàng)建了有限個(gè)listener,
    但是在這種情況下綁定上的數(shù)據(jù)只有View創(chuàng)建時(shí)的7個(gè),之后不為空的情況下沒有更換listener導(dǎo)致重用的view數(shù)據(jù)是新的,listener里面的position依然是過去創(chuàng)建view時(shí)的7個(gè)之一,不會(huì)變化(注意參數(shù)上的final)
    若在else里面再重新setListener,view是有重用,listener被換成新的,并與新的position綁定,老的listener就會(huì)變成一個(gè)廢對(duì)象,等待gc回收,隨著list滾動(dòng),越來越多
    關(guān)鍵是我們的業(yè)務(wù)與position這個(gè)參數(shù)有關(guān)
    1.重用?convertView?
    用以避免重復(fù)創(chuàng)建 View,重復(fù)創(chuàng)建 View 代價(jià)較大,而且如果重用 view 不改變寬高,重用View可以減少重新分配緩存造成的內(nèi)存頻繁分配/回收;

    2. 避免在 getView 中有 重復(fù)調(diào)用的 findViewById
    findViewById 的實(shí)現(xiàn)是遍歷,如果你定義的 View 越復(fù)雜代價(jià)越大。
    Google 推薦的做法是用 ViewHolder,然后保存在 view 的 tag 中。現(xiàn)在 RecyclerView 也是強(qiáng)制使用 ViewHolder 了。

    3. 設(shè)置 View (如 TextView#setText )之前先對(duì)比數(shù)據(jù)是否有改變
    一般來說,【比較兩個(gè)數(shù)據(jù)的代價(jià)】遠(yuǎn)小于【 View 的重繪的代價(jià)】

    4.?避免在 getView 函數(shù)中直接加載 Image 或做其他比較耗時(shí)的操作
    加載本地 Image 需要載入內(nèi)存以及解析 Bitmap ,都是比較耗時(shí)的操作。
    用戶快速滑動(dòng)列表時(shí),會(huì)大量調(diào)用 getView ,而 getView 是在主線程中被調(diào)用的。如果你在 getView 函數(shù)中直接加載 Image 或做其他耗時(shí)操作,就會(huì)造成滑動(dòng)比較卡。加載 ImageView 的解決方案就是開一個(gè)線程去把做這事。有很多第三庫可以做這事。

    5. ListView 中元素避免半透明
    半透明繪制需要大量乘法計(jì)算,在滑動(dòng)時(shí)不停重繪會(huì)造成大量的計(jì)算,在比較差的機(jī)子上會(huì)比較卡。在設(shè)計(jì)上能不半透明就不不半透明。實(shí)在要弄的話我個(gè)人是用個(gè)比較偷懶的方法,是在滑動(dòng)的時(shí)候把半透明設(shè)置成不透明,滑動(dòng)完再重新設(shè)置成半透明。

    6. 盡量開啟硬件加速
    硬件加速提升巨大,避免使用一些不支持的函數(shù)導(dǎo)致含淚關(guān)閉某個(gè)地方的硬件加速。
    當(dāng)然這一條不只是對(duì) ListView。

    7.用 ListView 威力加強(qiáng)版 -- RecyclerView
    更多的新武將,更多的姿勢(shì),更規(guī)范的使用,更好用的動(dòng)畫,更加強(qiáng)大的變化 ? 轉(zhuǎn)載自知乎(http://www.zhihu.com/question/19703384

    總結(jié)

    以上是生活随笔為你收集整理的Listview性能优化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。