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