Android優化UI篇

學習的過程當中善於總結才能快速提高我的的水平,特別做爲程序員。項目優化也是很重要的一部分,最近尋思着寫一篇文章總結,不管是對之後的開發或者提高自身的水平都有用。在寫這篇文章以前,結合自身項目經驗網上多篇博客,這裏作個總結,特別一些細節的原理,我也會在文末附上相關的連接,雖然有時候不須要了解原理。關於UI方面的優化不能光看一遍就完事了,主要仍是開始時主動去培養這樣的習慣,本篇的順序主要是依照UI優化重要等級寫的。android

過分繪製

你們應該都瞭解過,簡單說一下,接下來主要講解咱們實際開發中怎麼去避免這種現象。Overdraw(過分繪製)是指屏幕上的某個像素在同一幀的時間內被繪製了屢次。 git

過分繪製.png
若是當前區域被繪製兩次,就是過分繪製一次,以此類推,每繪製一次都會消耗性能CPU、GPU、還有電量等,因此做爲開發就是儘可能減小同一區域繪製次數。 過分繪製主要成因以下:

一、 因爲佈局複雜形成嵌套佈局 二、佈局中的view設置多層背景顏色程序員

佈局嵌套
  • 使用merge標籤
  • 使用RelativeLayout和ConstraintLayout(增強版RelativeLayout)
  • 自定義Viewgroup,重寫layout方法
多層背景

我這裏會分爲大概三種狀況:github

  • 單獨一個view,若是設置了一個前景圖片或者顏色,必定不要設置背景了,這種狀況大部分出如今imageview中
  • 對於viewgroup,若是不是特別須要,只設置最底層view的背景色
  • 全部的Activity都有默認的一層背景,默認主題顏色,這也是冷啓動出現黑白屏根源,若是是launcher類型Activity,能夠設置一張圖片或者顏色的drawable或者xml(建議不要這樣設置,xml解析會耗時),而且launcher類型Activity啓動以後要狀況背景,除去內存佔用。 launcher類:
<style name="AppTheme.Launcher">
    <item name="android:windowBackground">@drawable/launch_screens</item>
</style>
複製代碼

或者以下(不建議):bash

<style name="SplashTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowDisablePreview">true</item>
</style>
複製代碼

設置加載以後在oncreat()方法中再設置以下除去內存佔用網絡

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    //將window的背景圖設置爲空
    getWindow().setBackgroundDrawable(null);
    super.onCreate(savedInstanceState);
}
複製代碼

若是是普通Activity,則能夠:ide

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getWindow().setBackgroundDrawable(null);
    }
複製代碼

參考連接: juejin.im/post/5bf8f9…工具

##經常使用佈局性能比較佈局

幾種viewgroup性能對比.png

經過上圖總結幾點:post

  • 相同層級效果,優先級 FrameLayout>=LinearLayout>RelativeLayout>ConstraintLayout
  • 對於負責UI,首先優化考慮層級嵌套問題,優先使用ConstraintLayout和RelativeLayout
  • 使用LinearLayout時,慎用weight屬性和measureWithLargestChild屬性,會引發2次渲染
  • 儘可能減小ConstraintLayout和RelativeLayout中太多無效依賴,能夠減小沒必要要控件的刷新

自定義view優化

(還有須要總結的地方)

onDraw

自定義View裏面最重要,也是優化的最重要部分。

  • ondraw方法裏面儘可能不要去初始化對象,初始化過程放在構造方法裏,在ondraw方法裏面初始變量,若是須要invalidate(),則會一直調用ondraw(),會致使內存一直建立和回收,形成內存抖動。

  • 不要頻繁的設置可見於不可見方法,這樣也會調用,儘可能在xml設置的就是默認的狀態,減小ondraw方法調用。

  • 儘可能在View的內容發生改變的時候纔去觸發invalidate方法   

  • 儘可能使用ClipRect等方法來提升繪製的性能(重疊的部分不去繪製)。

  • 減小沒必要要元素的繪製

  • 不在屏幕的元素儘可能使用Canvas.quickReject把他們給剔除

onLayout

任什麼時候刻對View調用requestLayout()方法,都須要遍歷整個View樹,肯定每一個視圖它們所佔用的大小。若是在measure過程當中有任何衝突,可能會屢次遍歷。 若是UI設計師給的效果過於複雜,就須要自定義onlayout 方法,而且能夠減小嵌套問題,優化measure過程。

clipRect()

該方法用於裁剪畫布,調用clipRect()方法後,只會顯示被裁剪的區域,以外的區域將不會顯示。 該方法最後有一個參數Region.Op,表示與以前區域的區域間運算種類,若是沒有這個參數,則默認爲Region.Op.INTERSECT 這幾個參數的意義爲:

  • DIFFERENCE是第一次不一樣於第二次的部分顯示出來
  • REPLACE是顯示第二次的
  • REVERSE_DIFFERENCE 是第二次不一樣於第一次的部分顯示
  • INTERSECT交集顯示
  • UNION所有顯示
  • XOR補集 就是全集的減去交集生育部分顯示
  • clipxx方法只對設置之後的drawxx起做用,已經畫出來的圖形,是不會有做用的。

特殊標籤

include

include標籤經常使用於將佈局中的公共部分提取出來,好比頁面全部的actionbar那麼就能夠直接include進去了。 注意事項:

  • 外層能夠設置寬高,外層優於內層
  • 外層能夠設置id,外層id優先級高於內層id
  • 若是一個佈局中引用兩個id相同的include,則第一個有效,第二個無效,可是二者都會顯示。
merge:

merge標籤是做爲include標籤的一種輔助擴展來使用,它的主要做用是爲了防止在引用佈局文件時產生多餘的佈局嵌套。Android渲染須要消耗時間,佈局越複雜,性能就越差。如上述include標籤引入了以前的LinearLayout以後致使了界面多了一個層級。 注意事項:

  • merge必須放在佈局文件的根節點上。
  • merge並非一個ViewGroup,也不是一個View,它至關於聲明瞭一些視圖,等待被添加。
  • merge標籤被添加到A容器下,那麼merge下的全部視圖將被添加到A容器下。
  • 由於merge標籤並非View,因此在經過LayoutInflate.inflate方法渲染的時候, 第二個參數必須指定一個父容器,且第三個參數必須爲true,也就是必須爲merge下的視圖指定一個父親節點。
  • 若是Activity的佈局文件根節點是FrameLayout,能夠替換爲merge標籤,這樣,執行setContentView以後,會減小一層FrameLayout節點。
  • 自定義View若是繼承LinearLayout,建議讓自定義View的佈局文件根節點設置成merge,這樣能少一層結點。
  • 由於merge不是View,因此對merge標籤設置的全部屬性都是無效的。
  • 若是引用到外層的是LinearLayout ,merge內部方向跟隨LinearLayout 設置的方向
ViewStub:

ViewStub 直接繼承自View,默認是不可見的,沒有measure過程,只有加載的時候纔會由加載的xml替換掉(ViewStub只能inflate一次,再次進行inflate的時候會報異常)或者設置爲Visibility時纔可見。最大特色就是使用才加載。 這裏簡述一個經常使用的使用場景,有時候頁面沒有數據,會顯示無數據頁面或者網絡異常頁面,這種狀況就很好的利用了使用時纔去加載。

注意事項:

  • ViewStub只能被Inflate一次,inflate以後ViewStub對象就會被置爲空
  • ViewStub只能用來Inflate一個佈局文件,而不是某個具體的View(能夠把View寫在某個佈局文件中)
  • android:id——ViewStub 自身的Id,不管是否被inflate,均可以經過findViewById拿到對應的ViewStub控件自己。
  • android:inflatedId——ViewStub對用的layout裏面根節點的id,inflate以後能夠經過findViewById獲取到對應的被映射的佈局對象
  • viewStub.inflate()以後,若是要顯示或者隱藏佈局跟普通view同樣,可是千萬不要再一次.inflate()(會報異常)。
  • 在xml 中定義ViewStub 節點時,內部不能包含其餘節點,也就是說,ViewStub 是一個自閉合節點,若是一個佈局view若是想經過ViewStub顯示,只能定義在單獨的xml 文件中。
ViewStub於Gone的對比

設置爲GONE的View不會佔用佈局空間,可是會進行類的初始化;如ImageView 將src設置爲一個BitmapDrawable,那麼該圖片將會加載到內中 ViewStub只有在代碼中進行inflate以後纔會加載進來,不會佔用內存。

性能對比: blog.csdn.net/wolinxuebin…

space

Space 常常用於組件之間的縫隙,其draw()爲空,減小了繪製渲染的過程。組件之間的距離使用 Space 會提升了繪製效率,特別是對於動態設置間距會很方便高效。由於draw()爲空,對該 view 沒有作任務繪製渲染,因此不能對 Space 設置背景色,若是須要的間隔須要設置顏色是明顯不合適的。Space 相對於View設置間距的好處是不用draw,缺點是不能設置背景色。

其餘

  • 若是初始化View不可見的時候,使用View.GONE代替View.VISIBLE,設置GONE的view會加載的時候標記,不會去measure過程。

  • 減小alpha值對性能的影響 對於不透明的View,顯示它只須要渲染一次便可,但是若是這個View設置了alpha 值,會至少須要渲染兩次。緣由是包含alpha的view須要事先知道混合View的下一層元素是什麼,而後再結合上層的View進行Blend混色處理,而且對於設置。

  • TextView設置文字和圖片減小View的解析加載

  • 使用TextView設置換行功能,例如:

<TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="第一行\第二行" />
複製代碼
  • 對於肯定寬高的View或者ViewGroup,儘可能使用固定的寬高或者match_parent,例如:recyclerview若是 Item 高度是固定的話,可使用 RecyclerView.setHasFixedSize(true); 來避免 requestLayout 浪費資源;

附言

UI渲染
blog.csdn.net/lmj62356579… UI卡頓優化
blog.csdn.net/joye123/art…
UI優化工具Lint
blog.csdn.net/luzhenyuxfc…
UI優化工具Hierarchy Viewer
www.jianshu.com/p/e9e05ce5b… github.com/romainguy/V…

相關文章
相關標籤/搜索