Android中的4.0新佈局控件:Space和GridLayout

Android4.0 Ice Cream Sandwich ICS) 提供了兩種新的控件,也就是SpaceGridLayout,是專門爲大屏幕設備提供更豐富的用戶交互體驗而設計。

在這之前,Android中最常用的佈局類是LinearLayout,它能將它的子元素們水平排列或垂直排列。當界面佈局比較複雜的時候,也可以利用它嵌套一系列分割出來的LinearLayout子佈局來實現,嵌套的層數通常不宜太深,只適合於許多簡單佈局的情形。

嵌套佈局有很多顯著的缺點(參考 Android Layout Tricks #1Flattening The Stack 這些文章),總結起來有這三個方面:

  • 1.無法同時在水平和豎直方向對齊;
  • 2.嵌套太深影響性能;
  • 3.不適用於那些支持自由編輯的設計工具;

以下就是關於上述第一個問題的簡單的例子:

當文字字體和“Email address”標籤文字本身改變的時候,我們會希望標籤與它右邊的組件的底部基線對齊,同時讓它的右邊緣與它下方的標籤的右邊緣對齊。但如果用嵌套的線性佈局做這個會很困難,因爲標籤本身會去和其他組件在水平和豎直方向上自動對齊。

諸如此類的問題並不是第一次出現在Android或者更大範圍的UI工具上,但我們需要用它們豐富的平臺支持,來推動我們的其他工作。

GridLayout

爲了提供更好的佈局支持,我們已經在4.0框架中增加了一種新的佈局類即GridLayout,它通過將容器自身的真實區域切割成行列單元來解決上述問題。

正如上圖所示在使用GridLayout之後,“Email address”標籤就可以同時屬於那底部基線對齊的一行和那右邊緣對齊的一列。

GridLayout用一組無限細的直線將它的繪圖區域分割成行、列、單元。它支持行、列拼接合並,這就使得一個子元素控件能夠排布在一系列連續單元格組成的矩形區域。在下文中,我們將直接使用“行”、“列”、“單元格”這些術語來分別代表“行集合”、“列集合”、“單元格集合”,這裏集合的意思是指那些一個或多個連續元素。

LinearLayout的相似性

GridLayout的所有XML APILinearLayout有着一致的語法規則,所以如果你已經使用過LinearLayout的話,上手GridLayout也是應該很容易的。事實上,它們之間是非常相似的,相似到直接將XML文件中的標籤名從LinearLayout改到GridLayout而無需做其他改變,就可以實現與LinearLayout中相似的UI佈局。即使不是這樣,你也仍然能借此開啓使用這種二維佈局之路。

開始使用GridLayout

Android 4.0 SDK的程序示例中,有兩個示例分別演示瞭如何在Java代碼和XML中使用GridLayout

samples/ApiDemos/src/com/example/android/apis/view/GridLayout0.java

samples/ApiDemos/res/layout/grid_layout_1.xml

這裏有一個較上述XML佈局中稍微簡化版的XML代碼:

<?xml version=”1.0″ encoding=”utf-8″?>
<GridLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:useDefaultMargins=”true”
android:alignmentMode=”alignBounds”
android:columnOrderPreserved=”false”
android:columnCount=”4″
>

<TextView
android:text=”Email setup”
android:textSize=”32dip”
android:layout_columnSpan=”4″
android:layout_gravity=”center_horizontal”
/>

<TextView
android:text=”You can configure email in just a few steps:”
android:textSize=”16dip”
android:layout_columnSpan=”4″
android:layout_gravity=”left”
/>

<TextView
android:text=”Email address:”
android:layout_gravity=”right”
/>

<EditText
android:ems=”10″
/>

<TextView
android:text=”Password:”
android:layout_column=”0″
android:layout_gravity=”right”
/>

<EditText
android:ems=”8″
/>

<Space
android:layout_row=”4″
android:layout_column=”0″
android:layout_columnSpan=”3″
android:layout_gravity=”fill”
/>

<Button
android:text=”Next”
android:layout_row=”5″
android:layout_column=”3″
/>
</GridLayout>

在這裏你能注意到的第一個區別就是在以上例子中,子元素控件沒有使用WRAP_CONTENTMATCH_PARENT這種通常出現在Android佈局資源中的屬性常量。但在GridLayout中,你通常不需要使用這些屬性,這樣做的原因在GridLayout.LayoutParams的API文檔中有所解釋。

行與列的索引

第二個你會注意到的事是在上述XML資源中,子元素控件並不總是明確指定它們自身擺放在哪些單元格內。事實上GridLayout中每個子元素控件的佈局參數屬性中有行與列的索引,它們一起定義了這個控件該擺放在哪個位置,但當其中一個參數或這兩個參數都沒有指定的時候,GridLayout會提供默認值而不拋出異常。

自動索引的分配

當子元素控件被加入一個GridLayout的時候,GridLayout會維護這一個位置指針,以及一個所謂的“高水位標誌”,用來將控件擺放到那些還閒置着的單元格里去。

當GridLayout的方向屬性orientation被設置成水平(horizontal),且列數屬性columnCount 也設置過時(在上圖例子中,該值爲8),那麼高水位標誌(上圖中紅色標記的線)將會爲每一列維護一個獨立的高度值。當索引值需要創建時,GridLayout首先會通過檢查新控件的rowSpancolumnSpan屬性來計算所需單元格集的區域大小,然後從上圖中位置指針所指的位置開始,以從左到右,從上往下的順序遍歷可用空間,找到的第一個可用空間的行、列索引值。

假如GridLayout的方向屬性是豎直(vertical)的,所有的算法原則都與水平時一樣,只是將水平軸和豎直軸所扮演的角色互換一下。

如果你希望將多個視圖控件擺放在同一單元格內,你就必須要明確指定它的索引值了,因爲默認的分配過程正如上文解釋的那樣,是將控件擺在獨立的單元格內的。

大小、外邊距以及對齊方式

GridLayout中,定義控件大小、外邊距的做法與在LinearLayout中是一樣的。對齊、位置屬性(gravity)的使用方式也與LinearLayout中一樣,同樣使用lefttoprightbottomcenter_horizontalcenter_verticalcenterfill_horizontalfill_verticalfill這些常量值。

自適應性

與其他UI工具包着的網格佈局不同的是,GridLayout不會將數據綁定到行列中。相反的,所有與對齊和佈局自適應相關的東西都只跟組件自身相關。GridLayout將那些行爲模式從中剝離開來,提供了一個更加通用的系統來讓那些處於傳統深層次嵌套佈局中的但只有輕微聯繫的子元素們自適應到一個單一的佈局配置文件中。

那些單元列的自適應性可以從該列中組件的gravity屬性推斷得到,如果每個組件都定義了一個gravity屬性,那麼這個列就可以認爲是自適應的,否則只能認爲是不自適應的。更多關於這方面的細節可參考GridLayoutAPI文檔

模擬其他佈局

GridLayout並沒有加入Android平臺中其他佈局的所有特性,但它也擁有一系列非常豐富的特性,使得其他佈局中常用的特性也能被正常地在單一GridLayout中模擬出來。

儘管LinearLayout可以被當作是GridLayout的一個特例,但當一組視圖控件的佈局簡單到就是單一行或單一列對齊時,選擇用LinearLayout會更好,因爲它自身就能說明容器中子元素的排版表現目的,而且還有一些性能上的優勢(當然是非常小的)。

TableLayout佈局的模擬也是非常直接的,因爲GridLayout支持行、列合併。同時,TableRowsGridLayout中就不需要了。對於相同的UI,使用GridLayout完成起來往往更快而且消耗更少的內存空間。

簡單的RelativeLayout佈局通常就是將相互關聯的視圖控件組合成行與列,以此來實現網格。與標準的網格不同的是,GridLayout由系統來完成那些繁重的排版工作。通過設置GridLayoutrowOrderPreservedcolumnOrderPreserved屬性,就可以把GridLayout從傳統的網格排版中解放出來,完成大部分RelativeLayout所能完成的佈局——甚至是那種當子元素尺寸變化時要求網格線能相互交錯的效果。

簡單的FrameLayout排版也能夠在GridLayout的單元格內實現,因爲GridLayout的一個單元格可以放置多個視圖。要在兩個視圖之間切換,只需要將它們放在同一單元格內,在代碼中設置可見性屬性的值來切換。與LinearLayout提到的情形一樣,如果你所需要的功能就是如上描述的那樣,選擇使用FrameLayout會更好,因爲那樣會有少量性能上的優勢。

GridLayout缺少的一個重要特性就是按指定比例在行、列給子元素中分配額外空間,這個特性在LinearLayout中是能通過weight這個屬性實現的。此項遺漏以及實現它的可能性在GridLayoutAPI文檔中也有所提及。

視圖控件佈局操作的階段

將上文中討論的單元格索引分配階段與視圖控件佈局階段區分開是很有必要的。正常來講,單元格索引的分配階段只發生一次,也就是在UI初始化之後。索引分配只會在索引本身未指定時才發生,同時這個階段要確保所有視圖在佈局階段都已經有一系列已定義好的、將它擺放在哪裏的單元格。

而視圖控件佈局階段通常在索引分配階段之後進行,並且每當視圖的尺寸改變時都會重新計算其位置、尺寸。GridLayout會在佈局過程中測量每個子元素控件的尺寸,這樣就能計算它們在網格中所需要的寬和高值。當GridLayout使用gravity屬性最終擺放每個組件在單元格中的位置後,佈局階段纔算完成。

儘管索引的自動分配只發生一次,但從技術上來講GridLayout是一個動態佈局,這意味着如果你在組件佈局完成之後改變了它的方向,或者增加或者刪除某些子元素,GridLayout都會重複上述過程,重新分配索引,讓佈局重新以一合理的方式呈現。

站在性能的角度來看,瞭解到GridLayout的實現已經爲一些通用場景比如初始化一次但經常更新佈局這種場景做了優化是十分有必要的。因此,初始化步驟通常是設置一下內部數據結構,這樣佈局階段會快速完成而且不消耗任何內存。換句話說,也就是無論是改變GridLayout的方向還是改變它子元素的數量,都比其他常用佈局操作要更消耗性能。

結論

GridLayout吸納了很多Android框架中已有的通用目的的佈局的功能:也就是LinearLayoutFrameLayoutTableLayoutRelativeLayout的綜合功能。比如說它提供了將高度嵌套的視圖結構替換成一個單一的高度優化過的佈局的解決方案。

如果你剛開始使用Android UI,還不熟悉Android的佈局類型,那麼可以考慮使用GridLayout——它提供了其他佈局所擁有的大部分特性,並且擁有比TableLayoutRelativeLayout更通用的API

我們希望FrameLayoutLinearLayout以及GridLayout的結合能提供一系列足夠豐富的特性來完成大解決佈局工作而無需手寫代碼。當然花一些時間來決定優先使用哪一種佈局是非常有必要的,一個最佳的選擇可以減少中間容器的使用並且提供一個更快、消耗內存更少的用戶界面。

發佈了32 篇原創文章 · 獲贊 11 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章