Android中佈局的優化

筆墨Android發表於2018-04-17

我的掘金地址

#本文涉及到的知識點

  • 簡單的說明以下佈局優化的重要性
  • 一些佈局使用的注意事項
  • include、merge與ViewStub的使用
  • ConstraintLayout的使用

##1.佈局優化的重要性

在日常開發中對於佈局我們很少注重效能,主要注重的就是相應的展示問題。可能由於介面的複雜程度,使得介面巢狀的層級過多,這樣也就導致了繪製頁面的卡頓現象。增加了GPU渲染的複雜程度。這裡引用一張圖片來說明相應的問題。

GPU過度繪製參考圖

怎麼檢視呢?手機設定->開發者選項->除錯GPU過度渲染 開啟之後你就能看到你的APP到底有沒有過度繪製了。如果你看見的是一片紅,那麼恭喜你...你必須考慮相應的優化了!說了這麼多,其實最重要的就是減少層級、減少相應的過度繪製,就能有效的對佈局進行相應的優化。所以在Android做一些簡單的佈局優化還是有必要的。。。

##2.一些佈局使用的注意事項

這裡先提出個問題,大家想一下?如果專案中有這樣一個需求,豎直排列兩行每行兩個元素,大家怎麼去實現呢?相信有的人會使用相對佈局,有的人會使用線性佈局,那麼問題就來了,如果你使用線性佈局的話,就會存在佈局巢狀的問題,就平白的多了一層佈局巢狀,平時開發的時候這樣的問題很多,如果你多加註意的話,你會發現很多這樣的問題。

這裡我主要想說的是,如果在平時開釋出局的時候,如果在佈局不是很複雜的情況下儘量使用相對佈局,少用線性佈局。但是大家要注意一點,如果佈局複雜的話,或者層級較深的情況下,最好還是使用線性佈局。這裡不知道大家注意沒有,在Android Studio2.+版本的時候,所有預設的佈局都是線性佈局,為什麼呢?我就不在繼續深入講解了,感興趣的同學可以看看尹star 的->Android中RelativeLayout和LinearLayout效能分析主要是Measure在測量的時候產生的效能消耗!所以這個問題主要看你佈局是否比較複雜。。。其實在現在的版本中,google推薦我們使用的是ConstraintLayout(這個我會在後面詳細的講解)

##3.include、merge與ViewStub的使用

說到佈局優化問題,很難不提及include、merge與ViewStub的使用,但是這些都有相對的侷限性的,下面我們就來一一講解:

###3.1 include的使用和注意事項

這個東西相信大家都不陌生,它能幫我們解決佈局複用的問題,只要include匯入一個佈局就可以了。像這樣:

<include layout="@layout/XXX"/>
複製程式碼

XXX代表引入的佈局。是不是很簡單,那麼我問大家幾個問題?

  • 如果在include標籤中我設定相應的layoutxxx的屬性,會對原來的佈局有什麼影響?
  • 如果在include標籤中我設定了id,會對原來的佈局有什麼影響?

這些問題,相信細心的人應該會有體會的,但是對於有些人來說,或許很陌生。所以這裡有必要說明一下:

  • 當你在include中設定layoutxxx屬性的時候,會覆蓋相應匯入根佈局的layoutxxx屬性,怎麼理解呢?也就是說如果你匯入的佈局是這樣的
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#223344"
        android:gravity="center"
        android:text="匯入的佈局"
        android:textColor="@android:color/white"
        android:textSize="20sp" />

</LinearLayout>
複製程式碼

這裡看好,LinearLayout的寬和高是佔用父窗體。如果你要是這麼寫

    <include layout="@layout/test_view" />
複製程式碼

的話,會顯示全屏的,就像這個樣子

沒有設定layoutxxx的屬性

但是如果你要是寫成這個樣子就不一樣了

    <include
        layout="@layout/test_view"
        android:layout_width="200dp"
        android:layout_height="200dp" />
複製程式碼

會變成這個樣子

設定了layoutxxx的屬性

這裡要特別注意一下。。。

  • 當你在include中設定id的時候,會覆蓋相應匯入根佈局的id屬性,也就是說你只能使用include中的id屬性獲取根view了!

###3.2 merge的使用和注意事項

對於新手來說,這個標籤應該很少使用,至少我是這樣的,其實我呆了這麼多公司,也很少有人去用這個,可能我呆的都是小公司吧!哈哈。。。不閒扯了,其實這個標籤主要是針對include匯入的標籤的根佈局來說的,可以減少一個層級的巢狀!

先來說一下這個怎麼看啊!!!不說這個的都是扯淡。。。在Android Studio3.+版本上按照下圖獲取!

檢視檢視樹的工具

中間會彈出一個對話方塊,然後你就點確定,就能檢視相應的檢視樹了。。。

  • 當你沒有使用merge標籤的時候,佈局是這個樣子的!
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#223344"
        android:gravity="center"
        android:text="匯入的佈局"
        android:textColor="@android:color/white"
        android:textSize="20sp" />

</LinearLayout>
複製程式碼

這個時候你檢視一下檢視樹你會發現會出現下面這個樣子。

未使用merge標籤的檢視層級

我已經用紅框畫出來了,中間有一層LinearLayout作為根佈局,但是如果你把上面的LinearLayout換成merge標籤就不會有中間這層LinearLayout了。這個就不貼圖了,不信的你可以試試。。。

還有一個問題需要注意: 這裡有必要說明一下,如果你要給merge標籤設定一些屬性怎麼辦?其實是這樣的,你可以根據include的父控制元件進行屬性的設定,如果父控制元件是LinearLayout你就可以設定weidge或者相應LinearLayout的特有屬性...這也充分說明了為什麼include可以使用layoutxxx屬性覆蓋根佈局的layoutxxx屬性。

###3.3 ViewStub的使用和注意事項

其實關於這個ViewStub這個標籤,就相當於延時載入,但是有很多侷限性,打個比方說吧!相應在平時開發的時候,都會根據狀態進行一些控制元件的顯示和隱藏吧,一般的處理都是View的setVisibility();但是這裡如果你改變一次之後還能進行改變的話,那麼就不能使用ViewStub這個標籤。因為ViewStub對控制元件只有一次的改變,其實ViewStub在佈局載入的時候已經載入出來了,只是寬和高都是0罷了,當你inflate()的時候,再去載入這個整體的View,達到了延遲載入的目的!

基本的寫法就是這樣,也是匯入一個layout

    <ViewStub
        android:id="@+id/vs_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout="@layout/test_view" />
複製程式碼

什麼時候你想載入了,就呼叫View inflate = mVs.inflate();方法就能讓這個View顯示出來,但是一定要注意,這個方法只能使用一次,切記!切記!切記!重要的事情說三遍,當這個View載入出來的時候,你就可以像對待其他View對待它了,如果你要對這個View做一些特有控制元件的操作,最好強轉一下。。。

基本上上面這三個View優化的標籤我總結的就這麼多,有什麼不對的還請指出,我會立即改正的!!!

##4. ConstraintLayout的使用

關於這個控制元件,其實google早在很早的時候就讓我們去嘗試著使用了,但是在1.0.2那個版本的時候(記不太清楚了)才作為根佈局使用的。

A ConstraintLayout is a ViewGroup which allows you to position and size widgets in a flexible way. Note: ConstraintLayout is available as a support library that you can use on Android systems starting with API level 9 (Gingerbread). As such, we are planning on enriching its API and capabilities over time. This documentation will reflect those changes.

這裡面引用了google的解釋,大概的意思是

ConstraintLayout繼承ViewGroup允許您以靈活的方式定位和調整小部件的大小。 ConstraintLayout可用作支援庫,您可以從API級別9(薑餅)開始在Android系統上使用該支援庫。因此,我們計劃在一段時間內豐富其API和功能。本文件將反映這些更改。

主要是約束內部控制元件位置的佈局,裡面包含關於相對位置的屬性:

  • layout_constraintLeft_toLeftOf 當前控制元件的左側和目標控制元件的左側對齊
  • layout_constraintLeft_toRightOf 當前控制元件的左側和目標控制元件的右側對齊
  • layout_constraintRight_toLeftOf
  • layout_constraintRight_toRightOf
  • layout_constraintTop_toTopOf
  • layout_constraintTop_toBottomOf
  • layout_constraintBottom_toTopOf
  • layout_constraintBottom_toBottomOf
  • layout_constraintStart_toEndOf
  • layout_constraintStart_toStartOf
  • layout_constraintEnd_toStartOf
  • layout_constraintEnd_toEndOf

其實屬性很多,估計讓你記你也記不住,所以總結一下:

這裡所有的都可以這麼去理解,layout_xxxA_toxxxB 理解為,當前控制元件的A側和目標控制元件的B側對齊,你可能還是不理解,拿第一個舉例說明下,就是當前控制元件的左側和目標控制元件的左側對齊,標記了兩個,其他的以此類推。這裡注意裡面設定的引數是目標控制元件的id/父容器只有這兩種可能! 如果你有兩個控制元件,左側的以父控制元件對齊,另一個控制元件以左側的控制元件對齊,這時候如果右側以父控制元件對齊的話,這個控制元件會在右側剩下的空間居中(像下面這樣),但是如果你要是不設定右側的屬性的話,那麼兩個會挨著!(是那個屬性拆散了它們!哈哈)

Android中佈局的優化

  • layout_constraintHorizontal_bias 水平偏差百分比
  • layout_constraintVertical_bias 豎直偏差的百分比

這兩個屬性是針對於上面這個產生偏差的(只能設定以為小數,最大是1),其實我試過,只要你左右或者上下都有約束的時候,這個相應的屬性才生效,否則是無效的(如:一個控制元件左右都有約束,那麼設定layout_constraintHorizontal_bias才會生效,否則是不會生效的,豎直方向的同理)

  • android:layout_marginStart
  • android:layout_marginEnd
  • android:layout_marginLeft
  • android:layout_marginTop
  • android:layout_marginRight
  • android:layout_marginBottom

如果你設定了相應的margin_xxx屬性,那麼主要是產生了相應的邊距,和其他的控制元件都一樣,這裡就不再多做解釋了.

  • layout_goneMarginStart
  • layout_goneMarginEnd
  • layout_goneMarginLeft
  • layout_goneMarginTop
  • layout_goneMarginRight
  • layout_goneMarginBottom

這裡說明一個問題,當你有一個View處於GONE的情況,其他View還以來這個View來進行佈局,那麼就需要設定上面的goneMarginXXX來設定相應的Margin**(PS:這個只有在你依賴的那個約束為GONE的時候才會生效的屬性)**

  • android:minWidth 設定佈局的最小寬度
  • android:minHeight 設定佈局的最小高度
  • android:maxWidth 設定佈局的最大寬度
  • android:maxHeight 設定佈局的最大高度

這幾個屬性沒有什麼好說的了!

  • layout_constraintDimensionRatio 設定寬高比

這個屬性很有意思:設定相應的寬高比,但是我試了,至少要有三面有約束,否則不起作用,並且寬度和高度都應該設定成"0",引數值可以是"1:1"或者"h,1:1"也可以是"w,1:1"其中h代表限制高度,w代表限制寬度.這裡的比例不管你前面是什麼,後面的比值都是"寬度:高度"

  • layout_constraintHorizontal_weight
  • layout_constraintVertical_weight

這兩個屬性相當於LinearLayout的weight屬性和LinearLayout的屬性設定是一樣的

  • layout_constraintHorizontal_chainStyle
  • layout_constraintVertical_chainStyle
    • CHAIN_SPREAD 預設樣式(如果某些小部件設定為MATCH_CONSTRAINT,則它們將拆分可用空間)
    • CHAIN_SPREAD_INSIDE 但鏈條的終點不會被分散
    • CHAIN_PACKED 鏈條的元素將被打包在一起。

樣式說明

這裡我用的時候踩了一個大坑,這裡所謂的鏈式,你必須確保從左至右或者從上至下多有的約束都連著,這裡的連著是都有相應的約束: 如view a 和view b在豎直方向上形成鎖鏈:a的屬性設定為 top_toTopOf = “parent” bottom_toTopOf = “b” b的屬性設定為top_toBottomOf=“a” bottom_toBottom = “parent"切記!切記!切記!都要連上....重要的事情說三遍,否則你是看不見效果的.效果就是上面這張圖的效果!

這裡使用了這個佈局,就能減少佈局的巢狀,也使得繪製的時候能減少GPU的負擔,這樣就能達到佈局優化的目的了!!!

上面這些......基本上就是我在專案中優化佈局時候所做的一些優化,有什麼不妥的地方還請指正!


歡迎關注我的公眾號

相關文章