Layouting in Android
The layouting cycle (measure and arrange) in Uno on Android involves a complex interaction between Android UI framework methods and Uno methods. These interactions are summarized in the diagram below. This information is primarily intended to help when debugging Uno, but may be interesting to anyone curious as to how native Android methods are connected to the UWP contract exposed by Uno.
flowchart TD
%% Android layout flow
subgraph Measure Invalidation
uielement.invalidatearrange["UIElement.InvalidateArrange()"]
uielement.invalidatemeasure["UIElement.InvalidateMeasure()"]
base.requestlayout{{"View.RequestLayout()"}}
root.requestlayout{{"ViewRootImpl.RequestLayout()"}}
root.scheduletraversal{{"ViewRootImpl.ScheduleTraversals()"}}
uielement.invalidatearrange -. "actually calls" .-> uielement.invalidatemeasure
uielement.invalidatemeasure == set IS_DIRTY ==> base.requestlayout
uielement.invalidatemeasure -. parent: set IS_DIRTY_PATH .-> uielement.invalidatemeasure
base.requestlayout -. internally on parent .-> base.requestlayout
base.requestlayout ==> root.requestlayout
root.requestlayout ==> root.scheduletraversal
end
root.scheduletraversal -. on next animation loop .-> loop
subgraph loop["UI Loop Scheduling"]
root.performtraversal{{"ViewRootImpl.PerformTraversal()"}}
root.performtraversal ==> root.performmeasure
root.performtraversal ==> root.performlayout
subgraph Measure Phase
root.performmeasure{{"ViewRootImpl.PerformMeasure()"}}
view.measure{{"View.Measure()"}}
view.onmeasure{{"View.OnMeasure()"}}
onmeasure["(override) FrameworkElement.OnMeasure()"]
layouter.domeasure["ILayouterElement.DoMeasure()"]
layouter.measureoverride["Layouter.MeasureOverride()"]
measureoverride[["(override) Element.MeasureOverride()"]]
measureelement["FrameworkElement.MeasureElement()"]
layouter.measurechild["Layouter.MeasureChild()"]
layouter.measurechildoverride["Layouter.MeasureChildOverride()"]
view.setmeasureddimension{{"View.SetMeasuredDimension()"}}
view.layout{{"View.Layout()"}}
root.performmeasure == top-level element ==> view.measure
view.measure ==> view.onmeasure
view.onmeasure ==> onmeasure
onmeasure ==> layouter.domeasure
layouter.domeasure == IS_DIRTY set ==> layouter.measureoverride
layouter.domeasure == "IS_DIRTY_PATH set:<br>call for children<br>with previous availableSize" ==> layouter.measurechild
layouter.measureoverride ==> measureoverride
measureoverride == "for children (generally)" ==> measureelement
measureelement ==> layouter.measurechild
layouter.measurechild ==> layouter.measurechildoverride
layouter.measurechildoverride ==> view.layout
layouter.measurechildoverride -. "(return value will set)" .-> view.setmeasureddimension
view.layout ==> view.measure
end
view.layout -..-> arrange
subgraph arrange ["Arrange Phase"]
root.performlayout{{"View.PerformLayout()"}}
view.onlayout{{"View.OnLayout()"}}
unoviewgroup.onlayoutcore["UnoViewGroup.OnLayoutCore() - abstract"]
onlayoutcore["(override) FrameworkElement.OnLayoutCore()"]
layouter.arrange["Layouter.Arrange()"]
layouter.arrangeoverride["Layouter.ArrangeOverride()"]
arrangeoverride[["Element.ArrangeOverride()"]]
arrangeelement["FrameworkElement.ArrangeElement(child)"]
layouter.arrangechild["Layouter.ArrangeChild()"]
layouter.arrangechildoverride["Layouter.ArrangeChildOverride()"]
root.performlayout == x ==> view.onlayout
view.onlayout == "through OnLayout() override" ==> unoviewgroup.onlayoutcore
unoviewgroup.onlayoutcore ==> onlayoutcore
onlayoutcore ==> layouter.arrange
layouter.arrange ==> layouter.arrangeoverride
layouter.arrangeoverride ==> arrangeoverride
arrangeoverride == " for children (generally) " ==> arrangeelement
arrangeelement ==> layouter.arrangechild
layouter.arrangechild ==> layouter.arrangechildoverride
layouter.arrangechildoverride -..-> view.onlayout
end
end
subgraph legend
direction LR
uno-legend["Uno methods"]
native-legend{{"Native (Android) methods"}}
application-legend[["Application/Framework implementation"]]
end