第二十六章:自定义布局(八)

简介: 失效假设您已在页面上组装了一些布局和视图,并且由于某种原因,代码隐藏文件(或者可能是触发器或行为)会更改Button的文本,或者可能只是字体大小或属性。 该更改可能会影响按钮的大小,这可能会对页面其余部分的布局更改产生连锁反应。

失效
假设您已在页面上组装了一些布局和视图,并且由于某种原因,代码隐藏文件(或者可能是触发器或行为)会更改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的调用将不执行任何操作。

目录
相关文章
|
iOS开发
iOS流布局UICollectionView系列四——自定义FlowLayout进行瀑布流布局(一)
iOS流布局UICollectionView系列四——自定义FlowLayout进行瀑布流布局
401 0
iOS流布局UICollectionView系列四——自定义FlowLayout进行瀑布流布局(一)
|
存储 iOS开发
iOS流布局UICollectionView系列五——圆环布局的实现
iOS流布局UICollectionView系列五——圆环布局的实现
306 0
iOS流布局UICollectionView系列五——圆环布局的实现
|
iOS开发
iOS流布局UICollectionView系列三——使用FlowLayout进行更灵活布局(一)
iOS流布局UICollectionView系列三——使用FlowLayout进行更灵活布局
295 0
iOS流布局UICollectionView系列三——使用FlowLayout进行更灵活布局(一)
|
iOS开发
iOS流布局UICollectionView系列四——自定义FlowLayout进行瀑布流布局(二)
iOS流布局UICollectionView系列四——自定义FlowLayout进行瀑布流布局
281 0
|
JavaScript Android开发
第二十六章:自定义布局(十)
不允许无约束的尺寸!有时您希望在屏幕上看到所有内容,可能是一系列大小统一的行和列。您可以使用带有星号定义的所有行和列定义的Grid执行类似的操作,以使它们具有相同的大小。唯一的问题是您可能还希望行数和列数基于子节点数,并针对屏幕空间的最佳使用进行了优化。
842 0
|
存储 缓存 JavaScript
第二十六章:自定义布局(九)
编码的一些规则从上面的讨论中,您可以为自己的Layout 衍生物制定几个规则:规则1:如果布局类定义了诸如间距或方向等属性,则这些属性应由可绑定属性支持。 在大多数情况下,这些可绑定属性的属性更改处理程序应调用InvalidateLayout。
2077 0
|
JavaScript Android开发
第二十六章:自定义布局(七)
垂直和水平定位简化在VerticalStack中,LayoutChildren覆盖的末尾是一个switch语句,它有助于根据子级的HorizontalOptions属性设置水平定位每个子级。 这是整个方法: public class VerticalStack : Layout<View> { ...
875 0
|
JavaScript Android开发
第二十六章:自定义布局(六)
从Layout派生 我们现在拥有足够的知识来创建我们自己的布局类。布局中涉及的大多数公共和受保护方法都是由非泛型布局类定义的。 Layout 类派生自Layout,并将泛型类型约束为View及其派生类。
768 0
|
JavaScript Android开发 iOS开发
第二十六章:自定义布局(五)
内视过程中本章到目前为止提供的大部分信息都是从包含派生自各种元素(如StackLayout,ScrollView和Label)的类的测试程序汇编而来,覆盖虚拟方法(如GetSizeRequest,OnSizeRequest,OnSizeAllocated和LayoutChildren) ,并使用System.Diagnostics命名空间中的Debug.WriteLine方法在Visual Studio或Xamarin Studio的“输出”窗口中显示信息。
768 0
|
Android开发
第二十六章:自定义布局(四)
无限约束现在这里有一些标记,起初看起来与前面的例子非常相似,但有很大的不同: <ContentPage __ Padding="20"> <StackLayout> <Label Text="Sample text" /> __ </StackLayout> </ContentPage> ContentPage仍然使用参数(0,0,360,640)进行初始布局调用,而LayoutChildren覆盖的参数是(20,20,320,600)。
709 0