第 26 章的摘要。 自訂版面配置

Download Sample 下載範例

注意

這本書於2016年春季出版,此後一直沒有更新。 這本書中有很多仍然有價值,但一些材料已經過時,有些主題不再完全正確或完整。

Xamarin.Forms 包含衍生自 Layout<View>的數個類別:

  • StackLayout,
  • Grid,
  • AbsoluteLayout,以及
  • RelativeLayout.

本章說明如何建立衍生自 Layout<View>的類別。

版面配置的概觀

沒有處理 Xamarin.Forms 版面配置的集中式系統。 每個元素都負責判斷自己的大小應該是什麼,以及如何在特定區域內轉譯自己。

父項和子系

具有子系的每個元素都須負責將這些子系放在本身內。 最後決定其子系應該根據其可用大小和子系想要的大小,決定其子系的大小。

重設大小和定位

版面配置會從可視化樹狀結構的頂端開始,然後繼續進行所有分支。 配置中最重要的公用方法是由 LayoutVisualElement定義。 每個元素都是其他元素的父代,都會為其每個子系呼叫 Layout ,讓子系以值形式 Rectangle 相對於本身的大小和位置。 這些 Layout 呼叫會透過視覺化樹狀結構傳播。

必須呼叫 Layout ,元素才會出現在畫面上,並導致設定下列只讀屬性。 它們與 Rectangle 傳遞至 方法的 一致:

  • 型別 BoundsRectangle
  • 型別 Xdouble
  • 型別 Ydouble
  • 型別 Widthdouble
  • 型別 Heightdouble

Layout呼叫之前,HeightWidth具有 –1 的模擬值。

Layout 的呼叫也會觸發對下列受保護方法的呼叫:

最後,會引發下列事件:

方法 OnSizeAllocated 會由 PageLayout覆寫,這是中唯一可以有子系的 Xamarin.Forms 兩個類別。 覆寫的方法呼叫

LayoutChildren 接著會針對每個元素的子系呼叫 Layout 。 如果至少有一個子系有新的 Bounds 設定,則會引發下列事件:

條件約束和大小要求

若要 LayoutChildren 以智慧方式呼叫 Layout 其所有子系,它必須知道 子系的慣用想要 的大小。 因此,每個子系的呼叫 Layout 通常會在呼叫之前

出版書籍之後,方法 GetSizeRequest 已被取代,並取代為

方法 Measure 會容納 屬性, Margin 並包含 類型 MeasureFlag為的自變數,其有兩個成員:

對於許多元素, GetSizeRequestMeasure 從其轉譯器取得專案的原生大小。 這兩種方法都有寬度和高度 條件約束的參數。 例如, Label 會使用寬度條件約束來決定如何包裝多行文字。

和 都會GetSizeRequestMeasure傳回 類型的SizeRequest值,其具有兩個屬性:

通常這兩個值都相同,而且 Minimum 通常可以忽略該值。

VisualElement 也會定義類似於 GetSizeRequestGetSizeRequest呼叫的受保護方法:

該方法現在已被取代,並取代為:

衍生自 LayoutLayout<T> 的每個類別都必須覆寫 OnSizeRequestOnMeasure。 這是配置類別決定自己的大小,這通常是根據其子系的大小,呼叫 GetSizeRequestMeasure 子系取得。 在呼叫 OnSizeRequestOnMeasure之前和之後, GetSizeRequestMeasure 根據下列屬性進行調整:

無限條件約束

傳遞至 GetSizeRequest (或 Measure) 和 OnSizeRequest (或 OnMeasure) 的條件約束自變數可以是無限的 (亦即 的值 Double.PositiveInfinity)。 不過, SizeRequest 從這些方法傳回的 不能包含無限維度。

無限條件約束表示要求的大小應該反映元素的自然大小。 具有無限高度條件約束之子系的垂直 StackLayout 呼叫 GetSizeRequest (或 Measure)。 水平堆疊版面配置會在其子系上呼叫 GetSizeRequest (或 Measure) 並具有無限寬度限制。 AbsoluteLayout在其子系上呼叫GetSizeRequest具有無限寬度和高度條件約束的 (或Measure)。

查看進程內部

ExploreChildSize 會顯示簡單版面配置的條件約束和大小要求資訊。

衍生自版面配置<檢視>

自訂版面設定類別衍生自 Layout<View>。 它有兩個責任:

  • 覆寫 OnMeasure 以呼叫 Measure 配置的所有子系。 傳回配置本身的要求大小
  • 覆寫 LayoutChildren 以呼叫 Layout 所有版面配置子系

for這些覆寫中的 或 foreach 循環應該略過屬性IsVisible設定為 false的任何子系。

不保證對的呼叫 OnMeasureOnMeasure 如果版面配置父系控管版面配置的大小,則不會呼叫 (例如,填滿頁面的配置)。 基於這個理由, LayoutChildren 無法依賴呼叫期間取得的 OnMeasure 子大小。 通常, LayoutChildren 必須自行呼叫 Measure 配置子系,或者您可以實作某種大小的快取邏輯(稍後要討論)。

簡單的範例

VerticalStackDemo 範例包含簡化的VerticalStack類別及其使用示範。

簡化垂直和水準定位

必須在覆寫期間LayoutChildren執行的其中一個作業VerticalStack發生。 方法會使用子系的 HorizontalOptions 屬性來決定如何將子系放在 其位置中 VerticalStack。 您可以改為呼叫靜態方法 Layout.LayoutChildIntoBoundingRect。 這個方法會在子系上呼叫 Measure ,並使用 其 HorizontalOptionsVerticalOptions 屬性,將子系放置在指定的矩形內。

失效

元素屬性的變更通常會影響該專案在版面配置中的顯示方式。 配置必須失效,才能觸發新的版面配置。

VisualElement 定義受保護的方法,這個方法 InvalidateMeasure通常是由任何可系結屬性的屬性變更處理程式所呼叫,其變更會影響元素的大小。 方法 InvalidateMeasure 會引發 MeasureInvalidated 事件。

類別 Layout 會定義名為 InvalidateLayout的類似受保護方法, Layout 其衍生工具應該針對會影響其位置及大小子系之方式的任何變更呼叫。

撰寫版面配置的某些規則

  1. 衍生專案定義的 Layout<T> 屬性應該受到可繫結屬性的支援,而屬性變更的處理程式應該呼叫 InvalidateLayout

  2. Layout<T>定義附加可系結屬性的衍生項目應該覆寫OnAdded,將屬性變更的處理程式新增至其子系,並OnRemoved移除該處理程式。 處理程式應該檢查這些附加可系結屬性中的變更,並藉由呼叫 InvalidateLayout來回應。

  3. Layout<T>實作子大小快取的衍生專案,在呼叫這些方法時,應該覆寫InvalidateLayoutOnChildMeasureInvalidated清除快取。

具有屬性的版面配置

WrapLayout Book.Toolkit 中的 Xamarin.Forms類別會假設其所有子系的大小都相同,並將子系從一個數據列(或數據行)包裝到下一個數據列。 它會定義 Orientation 屬性,例如 StackLayout、 和 ColumnSpacingRowSpacingGrid屬性,而且會快取子大小。

PhotoWrap 範例會在 中ScrollView放入 WrapLayout 來顯示股票相片。

不允許不受限制的維度!

Book.Toolkit 連結庫中的Xamarin.Forms 是用來顯示其本身的所有子系。UniformGridLayout 因此,它無法處理不受限制的維度,如果遇到例外狀況,就會引發例外狀況。

PhotoGrid 範例示範UniformGridLayout

Triple screenshot of Photo Grid

重疊子系

Layout<T>衍生專案可以與其子系重疊。 不過,子系會依集合的順序 Children 轉譯,而不是呼叫其 Layout 方法的順序。

類別 Layout 會定義兩個方法,可讓您在集合內移動子系:

對於重疊的子系,集合結尾的子系會在集合開頭以視覺方式出現在子系的頂端。

Book.Toolkit 連結庫中的Xamarin.Forms 類別會定義附加屬性來指出轉譯順序,因此允許其中一個子系顯示在其他子系之上。OverlapLayout StudentCardFile 範例會示範下列專案:

Triple screenshot of Student Card File Grid

其他附加的可系結屬性

Book.Toolkit 連結庫中的Xamarin.Forms 類別會定義附加的可系結屬性,以指定兩Point個值和粗細值,並操作BoxView元素以類似線條。CartesianLayout

UnitCube 範例會使用該範例來繪製 3D Cube。

版面配置與版面配置

Layout<T>衍生專案可以呼叫 LayoutTo 而不是Layout以動畫顯示版面配置。 類別 AnimatedCartesianLayout 會執行此動作, 且 AnimatedUnitCube 範例會示範它。