第二十六章:自定義佈局(八)

失效
假設您已在頁面上組裝了一些佈局和視圖,並且由於某種原因,代碼隱藏文件(或者可能是觸發器或行爲)會更改Button的文本,或者可能只是字體大小或屬性。 該更改可能會影響按鈕的大小,這可能會對頁面其餘部分的佈局更改產生連鎖反應。
頁面上元素的更改觸發新佈局的過程稱爲失效。 當頁面上的某些內容無效時,表示它不再具有正確的大小或位置。 需要新的佈局週期。
失效過程從VisualElement定義的受保護虛擬方法開始:

protected virtual void InvalidateMeasure()

此方法受到保護。 您不能使外部代碼中的元素無效。 元素必須使自身無效,通常是在元素的屬性發生變化時。 這通常發生在可綁定屬性的實現中。 每當元素的可綁定屬性之一發生更改(可能導致元素的新大小)時,屬性更改的處理程序通常會調用InvalidateMeasure。
InvalidateMeasure方法觸發事件,以便在元素不再具有正確大小時通知元素外部的任何對象:

public event EventHandler MeasureInvalidated;

元素的父級通常處理此MeasureInvalidated事件。 但是,除了觸發此事件之外,該元素不會執行任何操作。 它不會改變自己的佈局大小。 這是元素父母的責任。 但是任何對GetSizeRequest的調用都將反映出新的大小。
VisualElement本身定義了28個公共屬性,但只有少數公開屬性觸發對InvalidateMeasure的調用以及隨後觸發MeasureInvalidated事件。 這些屬性是:

  • IsVisible
  • WidthRequest 和 MinimumWidthRequest
  • HeightRequest 和 MinimumHeightRequest

這些是VisualElement定義的唯一屬性,這些屬性會導致更改元素的佈局大小。
VisualElement定義了一些可能導致元素外觀發生變化但不會更改佈局大小的屬性。 這些是BackgroundColor,IsEnabled,IsFocused和Opacity。 對這些屬性的更改不會導致對InvalidateMeasure的調用。
此外,VisualElement定義了八個轉換屬性,這些屬性可以更改渲染元素的大小,但不會更改佈局中感知的元素大小。 這些是AnchorX,AnchorY,Rotation,RotationX,RotationY,Scale,TranslationX和TranslationY。
“行爲”,“樣式”和“觸發器”屬性可能間接影響佈局大小,但對這些屬性(或這些屬性維護的集合)的更改本身不會導致調用InvalidateMeasure。 此外,對InputTransparent,Navigation和Resources屬性的更改不會影響佈局大小。
然後有一個通過調用Layout設置的五個屬性。 這些是邊界,X,Y,寬度和高度。 這些屬性絕對不應該 - 並且不會 - 導致調用InvalidateMeasure。
View類爲VisualElement定義的屬性添加了三個屬性。 GestureRecognizers屬性不會影響佈局大小,但是對以下兩個屬性的更改會導致對InvalidateMeasure的調用:

  • HorizontalOption
  • VerticalOptions

每當屬性發生更改時,從View派生的類也會調用InvalidateMeasure,這可能會導致元素大小發生更改。 例如,只要以下任何屬性發生更改,Label就會調用InvalidateMeasure:

  • Text 和 FormattedText
  • FontFamily, FontSize, 和 FontAttributes
  • LineBreakMode

當TextColor屬性更改時,Label不會調用InvalidateMeasure。 這會影響文本的外觀,但不會影響文本的大小。 當HorizontalTextAlignment和VerticalTextAlignment屬性更改時,Label也不會調用InvalidateMeasure。 這些屬性控制文本在Label總大小內的對齊方式,但它們不會影響Label本身的大小。
Layout類以幾種關鍵方式構建在失效基礎結構上。 首先,Layout定義了一個類似於InvalidateMeasure的方法,名爲InvalidateLayout:

protected virtual void InvalidateLayout()

每當進行更改時,佈局衍生類應調用InvalidateLayout,該更改會影響佈局類如何定位和調整其子項的大小。
每當在其Content屬性(在ContentView,Frame和ScrollView的情況下)或其Children集合(在Layout 衍生物的情況下)添加或刪除子項時,Layout類本身都會調用InvalidateLayout。
如果您不希望佈局類在添加或刪除子項時調用InvalidateLayout,則可以覆蓋ShouldInvalidateOnChildAdded和ShouldInvalidateOnChildRemoved方法,只返回false而不是true。 然後,您的類可以在添加或刪除子項時實現自定義過程。 Layout 類會覆蓋由Element類定義的名爲OnChildAdded和OnChildRemoved的虛擬方法,但是您的類應該覆蓋OnAdded和OnRemoved方法以進行自定義處理。
此外,Layout類爲添加到其Content屬性或Children集合的每個子項設置MeasureInvalidated事件的處理程序,並在刪除子項時分離處理程序。 Page類做​​了類似的事情。如果要在觸發這些事件時收到通知,則Page和Layout類都會顯示可覆蓋的OnChildMeasureInvalidated方法。
這些MeasureInvalidated事件處理程序實際上是該過程的關鍵部分,因爲視覺樹中具有子項的每個元素都會在其子項之一發生更改時發出警報。這就是視覺樹中非常深的元素大小的變化如何導致樹的波動。
但是,Layout類會嘗試限制子項大小的更改對頁面總佈局的影響。如果特定佈局的大小受到限制,則子項大小的更改不會影響任何高於可視樹中此佈局的內容。
在大多數情況下,佈局大小的更改會影響佈局如何排列其子項。因此,佈局大小的任何更改都會導致佈局的佈局週期。佈局將調用其OnSizeRequested和LayoutChildren方法。
然而,相反並非總是如此。佈局排列其子項的方式可能會影響佈局的大小,或者可能不會。最明顯的是,如果佈局的大小受到完全約束,佈局的大小將不受佈局如何排列其子項的影響。
當佈局定義自己的屬性(例如StackLayout定義的Spacing和Orientation屬性)時,這種差異變得很重要。當此類屬性更改值時,佈局必須使其自身無效以導致發生新的佈局循環。佈局應該調用InvalidateMeasure還是InvalidateLayout?
在大多數情況下,佈局應調用InvalidateLayout。這保證了佈局可以調用其LayoutChildren方法,即使佈局的大小完全受限。如果佈局調用InvalidateMeasure,則僅當佈局的大小不完全受限時纔會生成新的佈局過程。如果佈局的大小受限,則對InvalidateMeasure的調用將不執行任何操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章