Zusammenfassung für Kapitel 26: Benutzerdefinierte Layouts
Hinweis
Dieses Buch wurde im Frühjahr 2016 veröffentlicht und seitdem nicht aktualisiert. Wenngleich ein großer Teil des Buchs weiterhin relevante Informationen liefert, sind einige Abschnitte veraltet, und einige Themen sind nicht mehr korrekt oder vollständig.
Xamarin.Forms umfasst eine Reihe von Klassen, die von Layout<View>
abgeleitet sind:
StackLayout
,Grid
,AbsoluteLayout
undRelativeLayout
.
In diesem Kapitel wird beschrieben, wie Sie eigene Klassen erstellen, die von Layout<View>
abgeleitet werden.
Das Layout im Überblick
Es gibt kein zentralisiertes System zur Verarbeitung des Xamarin.Forms-Layouts. Jedes Element bestimmt die eigene Größe selbst und ermittelt, wie es innerhalb eines bestimmten Bereichs gerendert wird.
Über- und untergeordnete Elemente
Jedes Element, das über untergeordnete Elemente verfügt, ist für die Positionierung dieser untergeordneten Elemente verantwortlich. Die Größe der untergeordneten Elemente wird dabei vom übergeordneten Element basierend auf der verfügbaren Größe und der vom untergeordneten Element angestrebten Größe bestimmt.
Festlegung der Größe und Positionierung
Für das Layout wird im oberen Bereich der visuellen Struktur mit der Seite begonnen, auf die alle Branches folgen. Die wichtigste öffentliche Methode innerhalb des Layouts ist Layout
, die durch VisualElement
definiert wird. Jedes übergeordnete Element ruft für jedes seiner untergeordneten Elemente Layout
auf, um die Größe des Elements festzulegen und seine Position zu bestimmen (relativ zum übergeordneten Element). Zu diesem Zweck wird ein Rectangle
-Wert angegeben. Diese Aufrufe von Layout
werden innerhalb der visuellen Struktur propagiert.
Damit ein Element auf dem Bildschirm angezeigt wird, muss Layout
aufgerufen werden. Dadurch werden die folgenden schreibgeschützten Eigenschaften festgelegt. Diese Eigenschaften stimmen mit dem Rectangle
-Wert überein, der an die Methode übergeben wird:
Bounds
vom TypRectangle
X
vom Typdouble
Y
vom Typdouble
Width
vom Typdouble
Height
vom Typdouble
Vor dem Layout
Aufruf Height
und Width
haben Simulierte Werte von –1.
Durch den Aufruf von Layout
wird zudem der Aufruf der folgenden geschützten Methoden ausgelöst:
SizeAllocated
, die wiederum zum Aufruf vonOnSizeAllocated
führt, die außer Kraft gesetzt werden kann.
Schließlich wird das folgende Ereignis ausgelöst:
Die Methode OnSizeAllocated
wird durch Page
und Layout
überschrieben, den einzigen beiden Klassen in Xamarin.Forms, die über untergeordnete Elemente verfügen können. Die außer Kraft gesetzten Methodenaufrufe
UpdateChildrenLayout
fürPage
-Ableitungen undUpdateChildrenLayout
fürLayout
-Ableitungen. Dadurch wird folgender Aufruf ausgelöst:LayoutChildren
fürPage
-Ableitungen undLayoutChildren
fürLayout
-Ableitungen.
LayoutChildren
ruft dann für jedes untergeordnete Element des Elements Layout
auf. Wenn mindestens ein untergeordnetes Element über eine neue Bounds
-Einstellung verfügt, wird das folgende Ereignis ausgelöst:
LayoutChanged
fürPage
-Ableitungen undLayoutChanged
fürLayout
-Ableitungen
Constraints und Größenanforderungen
Damit LayoutChildren
einen intelligenten Aufruf von Layout
für alle seine untergeordneten Elemente durchführen kann, muss eine bevorzugte oder gewünschte Größe der untergeordneten Elemente bekannt sein. Aus diesem Grund wird den Aufrufen von Layout
für die einzelnen untergeordneten Element in der Regel folgender Aufruf vorangestellt:
Nach der Veröffentlichung dieses Buchs wurde die Methode GetSizeRequest
durch folgende Methode ersetzt:
Die Methode Measure
umfasst die Margin
-Eigenschaft sowie ein Argument vom Typ MeasureFlag
, das über zwei Member verfügt:
IncludeMargins
None
, um Ränder auszuschließen
Bei vielen Elementen ruft GetSizeRequest
oder Measure
die native Größe des Elements von seinem Renderer ab. Beide Methoden verfügen über Constraints für Breite und Höhe. Label
verwendet z.B. die Breitenbeschränkung, um zu ermitteln, wie mehrere Textzeilen umbrochen werden.
Sowohl GetSizeRequest
als auch Measure
geben einen Wert vom Typ SizeRequest
zurück, der über zwei Eigenschaften verfügt:
Diese beiden Werte sind häufig identisch, und der Minimum
-Wert kann üblicherweise ignoriert werden.
VisualElement
definiert zudem eine geschützte Methode, die GetSizeRequest
ähnelt und von GetSizeRequest
aufgerufen wird:
OnSizeRequest
gibt einenSizeRequest
-Wert zurück
Diese Methode ist inzwischen veraltet und wurde wie folgt ersetzt:
Jede Klasse, die von Layout
oder Layout<T>
abgeleitet wird, muss OnSizeRequest
oder OnMeasure
außer Kraft setzen. An dieser Stelle bestimmt eine Layoutklasse ihre eigene Größe, die im Allgemeinen auf der Größe ihrer untergeordneten Elemente basiert, die sie durch einen Aufruf von GetSizeRequest
oder Measure
für die untergeordneten Elemente erhält. Vor und nach dem Aufruf von OnSizeRequest
oder OnMeasure
nimmt GetSizeRequest
oder Measure
basierend auf den folgenden Eigenschaften Anpassungen vor:
WidthRequest
vom Typdouble
, wirkt sich auf dieRequest
-Eigenschaft vonSizeRequest
ausHeightRequest
vom Typdouble
, wirkt sich auf dieRequest
-Eigenschaft vonSizeRequest
ausMinimumWidthRequest
vom Typdouble
, wirkt sich auf dieMinimum
-Eigenschaft vonSizeRequest
ausMinimumHeightRequest
vom Typdouble
, wirkt sich auf dieMinimum
-Eigenschaft vonSizeRequest
aus
Unbegrenzte Constraints
Die an GetSizeRequest
(oder Measure
) und OnSizeRequest
(oder OnMeasure
) übergebenen Argumente können unbegrenzt sein (d.h. Werte von Double.PositiveInfinity
). Der von diesen Methoden zurückgegebene SizeRequest
-Wert kann jedoch keine unbegrenzten Dimensionen enthalten.
Unbegrenzte Constraints geben an, dass die angeforderte Größe der natürlichen Größe des Elements entsprechen soll. Ein vertikales StackLayout
ruft GetSizeRequest
(oder Measure
) für seine untergeordneten Elemente mit einer unbegrenzten Höheneinschränkung auf. Ein horizontales Stapellayout ruft GetSizeRequest
(oder Measure
) für seine untergeordneten Elemente mit einer unbegrenzten Breiteneinschränkung auf. Ein AbsoluteLayout
ruft GetSizeRequest
(oder Measure
) für seine untergeordneten Elemente mit einer unbegrenzten Breiten- und Höheneinschränkung auf.
Der Prozess im Detail
ExploreChildSize zeigt Informationen zu Constraints und Größenanforderungen für ein einfaches Layout.
Ableiten aus der Layoutansicht<>
Eine benutzerdefinierte Layoutklasse wird von Layout<View>
abgeleitet. Sie hat zwei Aufgaben:
- Außerkraftsetzen von
OnMeasure
, umMeasure
für alle untergeordneten Elemente im Layout aufzurufen Zurückgeben einer angeforderten Größe für das Layout selbst - Außerkraftsetzen von
LayoutChildren
, umLayout
für alle untergeordneten Elemente im Layout aufzurufen
Die for
- oder foreach
-Schleife dieser Außerkraftsetzungen sollte alle untergeordneten Elemente überspringen, deren IsVisible
-Eigenschaft auf false
festgelegt ist.
OnMeasure
wird nicht in jedem Fall aufgerufen. Wenn das übergeordnete Element des Layouts die Größe des Layouts bestimmt (z.B. ein Layout, das eine Seite füllt), wird OnMeasure
nicht aufgerufen. Aus diesem Grund kann sich LayoutChildren
nicht darauf verlassen, dass die Größe der untergeordneten Elemente beim Aufruf von OnMeasure
bestimmt wird. Häufig muss LayoutChildren
den Aufruf von Measure
selbst für die untergeordneten Elemente des Layouts durchführen. Alternativ können Sie eine Logik zum Zwischenspeichern der Größe implementieren (dieses Thema wird später näher besprochen).
Ein einfaches Beispiel
Das Beispiel VerticalStackDemo enthält eine vereinfachte VerticalStack
-Klasse und zeigt, wie diese Klasse verwendet wird.
Vereinfachte vertikale und horizontale Positionierung
Eine der Aufgaben, die VerticalStack
ausführen muss, findet während der Außerkraftsetzung von LayoutChildren
statt. Die Methode ermittelt anhand der Eigenschaft HorizontalOptions
des untergeordneten Elements, wie das untergeordnete Element innerhalb von VerticalStack
positioniert werden soll. Sie können stattdessen die statische Methode Layout.LayoutChildIntoBoundingRect
aufrufen. Diese Methode ruft Measure
für das untergeordnete Element auf und positioniert das untergeordnete Element basierend auf den Eigenschaften HorizontalOptions
und VerticalOptions
innerhalb des angegebenen Rechtecks.
Invalidierung
Häufig wirkt sich eine Änderung der Eigenschaft eines Elements darauf aus, wie das Element im Layout angezeigt wird. Das Layout muss ungültig gemacht werden, um ein neues Layout zu starten.
VisualElement
definiert die geschützte Methode InvalidateMeasure
, die üblicherweise vom Handler für geänderte Eigenschaften einer bindbaren Eigenschaft aufgerufen wird, deren Änderung sich auf die Größe des Elements auswirkt. Die Methode InvalidateMeasure
löst ein MeasureInvalidated
-Ereignis aus.
Die Klasse Layout
definiert eine ähnliche geschützte Methode InvalidateLayout
, die von einer Layout
-Ableitung für jede Änderung aufgerufen werden sollte, die sich auf die Position und Größe ihrer untergeordneten Elemente auswirkt.
Regeln für die Layoutprogrammierung
Eigenschaften, die durch
Layout<T>
-Ableitungen definiert werden, sollten durch bindbare Eigenschaften gestützt werden, und die Handler für geänderte Eigenschaften solltenInvalidateLayout
aufrufen.Eine
Layout<T>
-Ableitung, die angefügte bindbare Eigenschaften definiert, sollteOnAdded
außer Kraft setzen, um einen Handler für geänderte Eigenschaften zu ihren untergeordneten Elementen hinzuzufügen, undOnRemoved
außer Kraft setzen, um diesen Handler zu entfernen. Der Handler sollte diese angefügten bindbaren Eigenschaften auf Änderungen überprüfen und ggf. mit einem Aufruf vonInvalidateLayout
reagieren.Ein
Layout<T>
Abgeleiteter, der einen Cache mit untergeordneten Größen implementiert, sollte den Cache überschreibenInvalidateLayout
undOnChildMeasureInvalidated
löschen, wenn diese Methoden aufgerufen werden.
Ein Layout mit Eigenschaften
Die Klasse WrapLayout
in der Bibliothek Xamarin.FormsBook.Toolkit setzt voraus, dass alle untergeordneten Elemente dieselbe Größe aufweisen, und führt für die untergeordneten Elemente einen Zeilen- oder Spaltenumbruch durch. Die Klasse definiert eine Orientation
-Eigenschaft wie StackLayout
sowie ColumnSpacing
- und RowSpacing
-Eigenschaften wie Grid
. Darüber hinaus wird mit dieser Klasse die Größe von untergeordneten Elementen zwischengespeichert.
Im Beispiel PhotoWrap wird ein WrapLayout
mit einer ScrollView
kombiniert, um Fotos anzuzeigen.
Dimensionen ohne Einschränkungen sind nicht zulässig!
UniformGridLayout
in der Bibliothek Xamarin.FormsBook.Toolkit ist so ausgelegt, dass alle untergeordneten Elemente innerhalb des Layouts angezeigt werden. Aus diesem Grund können keine Dimensionen ohne Einschränkungen verarbeitet werden. Wird eine solche Dimension erkannt, wird eine Ausnahme ausgelöst.
Das UniformGridLayout
wird im Beispiel PhotoGrid veranschaulicht:
Überlappende untergeordnete Elemente
Bei einer Layout<T>
-Ableitung ist eine Überlappung von untergeordneten Elementen möglich. Die untergeordneten Elemente werden jedoch in ihrer Reihenfolge in der Children
-Auflistung gerendert, nicht in der Reihenfolge, in der ihre Layout
-Methoden aufgerufen werden.
Die Klasse Layout
definiert zwei Methoden, mit denen Sie ein untergeordnetes Element innerhalb der Auflistung verschieben können:
LowerChild
, um ein untergeordnetes Element an den Anfang der Auflistung zu verschiebenRaiseChild
, um ein untergeordnetes Element ans Ende der Auflistung zu verschieben
Bei überlappenden untergeordneten Elementen werden untergeordnete Elemente am Ende der Auflistung oberhalb von untergeordneten Elementen am Anfang der Auflistung angezeigt.
Die Klasse OverlapLayout
in der Bibliothek Xamarin.FormsBook.Toolkit definiert eine angefügte Eigenschaft zur Angabe der Renderreihenfolge. Auf diese Weise kann eins der zugehörigen untergeordneten Elemente oberhalb von anderen Elementen angezeigt werden. Dies wird im Beispiel StudentCardFile veranschaulicht:
Weitere angefügte bindbare Eigenschaften
Die Klasse CartesianLayout
in der Bibliothek Xamarin.FormsBook.Toolkitdefiniert angefügte bindbare Eigenschaften, mit denen zwei Point
-Werte und ein Wert für die Dicke angegeben werden können. Außerdem werden BoxView
-Elemente so bearbeitet, dass sie Zeilen ähneln.
Im Beispiel UnitCube wird diese Klasse für einen 3D-Würfel verwendet.
Layout und LayoutTo
Eine Layout<T>
-Ableitung kann LayoutTo
anstelle von Layout
aufrufen, um das Layout zu animieren. Für diesen Vorgang wird die Klasse AnimatedCartesianLayout
verwendet, wie im Beispiel AnimatedUnitCube gezeigt.