public void Layout(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom, bool independent) { bool changed = true; if (!independent) { changed = SetFrame(left.AsRoundedValue(), top.AsRoundedValue(), right.AsRoundedValue(), bottom.AsRoundedValue()); } else { // If height or width specification is not explicitly defined, // the size of the owner view must be reset even the ExcludeLayouting is true. if (Owner.HeightSpecification < 0 || Owner.WidthSpecification < 0) { Owner.SetSize(right.AsRoundedValue() - left.AsRoundedValue(), bottom.AsRoundedValue() - top.AsRoundedValue()); } } // Check if Measure needed before Layouting if (changed || ((Flags & LayoutFlags.LayoutRequired) == LayoutFlags.LayoutRequired)) { OnLayout(changed, left, top, right, bottom); // Clear flag Flags &= ~LayoutFlags.LayoutRequired; } }
///<summary> /// Utility to reconcile a desired size and state, with constraints imposed by a MeasureSpecification. ///</summary> /// <param name="size"> How big the layout wants to be.</param> /// <param name="measureSpecification"> Constraints imposed by the parent.</param> /// <param name="childMeasuredState"> Size information bit mask for the layout's children.</param> /// <returns> A measured size, which may indicate that it is too small. </returns> /// <since_tizen> 6 </since_tizen> protected MeasuredSize ResolveSizeAndState(LayoutLength size, MeasureSpecification measureSpecification, MeasuredSize.StateType childMeasuredState) { var specMode = measureSpecification.Mode; LayoutLength specSize = measureSpecification.Size; MeasuredSize result = new MeasuredSize(size, childMeasuredState); switch (specMode) { case MeasureSpecification.ModeType.AtMost: { if (specSize.AsRoundedValue() < size.AsRoundedValue()) { result = new MeasuredSize(specSize, MeasuredSize.StateType.MeasuredSizeTooSmall); } break; } case MeasureSpecification.ModeType.Exactly: { result.Size = specSize; break; } case MeasureSpecification.ModeType.Unspecified: default: { break; } } return(result); }
private bool SetFrame(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom) { bool changed = false; if (_left != left || _right != right || _top != top || _bottom != bottom) { changed = true; } LayoutLength oldWidth = _right - _left; LayoutLength oldHeight = _bottom - _top; LayoutLength newWidth = right - left; LayoutLength newHeight = bottom - top; bool sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight); _left = left; _top = top; _right = right; _bottom = bottom; // Set actual positions of View. Owner.SetX(_left.AsRoundedValue()); Owner.SetY(_top.AsRoundedValue()); Owner.SetSize((int)newWidth.AsRoundedValue(), (int)newHeight.AsRoundedValue()); return(changed); }
void ForceUniformWidth(int count, MeasureSpecification heightMeasureSpec) { // Pretend that the linear layout has an exact size. MeasureSpecification uniformMeasureSpec = new MeasureSpecification(MeasuredWidth.Size, MeasureSpecification.ModeType.Exactly); for (int i = 0; i < count; ++i) { LayoutItem childLayout = _children[i]; if (childLayout != null) { LayoutLength desiredWidth = new LayoutLength(childLayout.Owner.WidthSpecification); LayoutLength desiredHeight = new LayoutLength(Owner.WidthSpecification); if (desiredWidth.AsRoundedValue() == LayoutParamPolicies.MatchParent) { // Temporarily force children to reuse their old measured height LayoutLength oldHeight = desiredHeight; childLayout.Owner.HeightSpecification = (int)childLayout.MeasuredHeight.Size.AsRoundedValue(); // Remeasure with new dimensions MeasureChildWithMargins(childLayout, uniformMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0)); childLayout.Owner.HeightSpecification = (int)oldHeight.AsRoundedValue(); } } } }
} // LayoutVertical void ForceUniformHeight(int count, MeasureSpecification widthMeasureSpec) { // Pretend that the linear layout has an exact size. This is the measured height of // ourselves. The measured height should be the max height of the children, changed // to accommodate the heightMeasureSpec from the parent MeasureSpecification uniformMeasureSpec = new MeasureSpecification(MeasuredHeight.Size, MeasureSpecification.ModeType.Exactly); for (int i = 0; i < count; ++i) { LayoutItem childLayout = _children[i]; if (childLayout != null) { LayoutLength desiredWidth = new LayoutLength(childLayout.Owner.WidthSpecification); LayoutLength desiredHeight = new LayoutLength(Owner.WidthSpecification); if (desiredHeight.AsRoundedValue() == LayoutParamPolicies.MatchParent) { // Temporarily force children to reuse their old measured width LayoutLength oldWidth = desiredWidth; childLayout.Owner.WidthSpecification = (int)childLayout.MeasuredWidth.Size.AsRoundedValue(); // Remeasure with new dimensions MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), uniformMeasureSpec, new LayoutLength(0)); childLayout.Owner.WidthSpecification = (int)oldWidth.AsRoundedValue(); } } } }
/// <summary> /// Measure the layout and its content to determine the measured width and the measured height.<br /> /// </summary> /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param> /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param> /// <since_tizen> 6 </since_tizen> protected override void OnMeasure( MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec ) { bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL; Extents padding = Owner.Padding; Extents margin = Owner.Margin; Interop.FlexLayout.FlexLayout_SetMargin(swigCPtr, Extents.getCPtr(margin)); Interop.FlexLayout.FlexLayout_SetPadding(swigCPtr, Extents.getCPtr(padding)); float width = FlexUndefined; // Behaves as WrapContent (Flex Auto) float height = FlexUndefined; // Behaves as WrapContent (Flex Auto) if( widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || widthMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost ) { width = widthMeasureSpec.Size.AsDecimal(); } if ( heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || heightMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost ) { height = heightMeasureSpec.Size.AsDecimal(); } Interop.FlexLayout.FlexLayout_CalculateLayout( swigCPtr, width, height, isLayoutRtl ); LayoutLength flexLayoutWidth = new LayoutLength( Interop.FlexLayout.FlexLayout_GetWidth(swigCPtr) ); LayoutLength flexLayoutHeight = new LayoutLength( Interop.FlexLayout.FlexLayout_GetHeight(swigCPtr) ); Debug.WriteLineIf( LayoutDebugFlex, "FlexLayout OnMeasure width:" + flexLayoutWidth.AsRoundedValue() + " height:" + flexLayoutHeight.AsRoundedValue() ); SetMeasuredDimensions( GetDefaultSize(flexLayoutWidth, widthMeasureSpec ), GetDefaultSize(flexLayoutHeight, heightMeasureSpec ) ); }
/// <summary> /// Assign a size and position to a layout and all of its descendants. <br /> /// This is the second phase of the layout mechanism. (The first is measuring). In this phase, each parent /// calls layout on all of its children to position them. This is typically done using the child<br /> /// measurements that were stored in the measure pass.<br /> /// </summary> /// <param name="left">Left position, relative to parent.</param> /// <param name="top">Top position, relative to parent.</param> /// <param name="right">Right position, relative to parent.</param> /// <param name="bottom">Bottom position, relative to parent.</param> /// <since_tizen> 6 </since_tizen> public void Layout(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom) { bool changed = SetFrame(left.AsRoundedValue(), top.AsRoundedValue(), right.AsRoundedValue(), bottom.AsRoundedValue()); // Check if Measure needed before Layouting if (changed || ((Flags & LayoutFlags.LayoutRequired) == LayoutFlags.LayoutRequired)) { OnLayout(changed, left, top, right, bottom); // Clear flag Flags &= ~LayoutFlags.LayoutRequired; } }
/// <summary> /// Measure the layout and its content to determine the measured width and the measured height.<br /> /// </summary> /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param> /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param> /// <since_tizen> 6 </since_tizen> protected override void OnMeasure( MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec ) { bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL; Extents padding = Owner.Padding; Extents margin = Owner.Margin; Interop.FlexLayout.FlexLayout_SetMargin(swigCPtr, Extents.getCPtr(margin)); Interop.FlexLayout.FlexLayout_SetPadding(swigCPtr, Extents.getCPtr(padding)); float width = FlexUndefined; // Behaves as WrapContent (Flex Auto) float height = FlexUndefined; // Behaves as WrapContent (Flex Auto) if( widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || widthMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost ) { width = widthMeasureSpec.Size.AsDecimal(); } if ( heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || heightMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost ) { height = heightMeasureSpec.Size.AsDecimal(); } // Save measureSpec to measure children. // In other Layout, we can pass it as parameter to measuring child but in FlexLayout we can't // because measurChild function is called by native callback. parentMeasureSpecificationWidth = widthMeasureSpec; parentMeasureSpecificationHeight = heightMeasureSpec; Interop.FlexLayout.FlexLayout_CalculateLayout( swigCPtr, width, height, isLayoutRtl ); LayoutLength flexLayoutWidth = new LayoutLength( Interop.FlexLayout.FlexLayout_GetWidth(swigCPtr) ); LayoutLength flexLayoutHeight = new LayoutLength( Interop.FlexLayout.FlexLayout_GetHeight(swigCPtr) ); Debug.WriteLineIf( LayoutDebugFlex, "FlexLayout OnMeasure width:" + flexLayoutWidth.AsRoundedValue() + " height:" + flexLayoutHeight.AsRoundedValue() ); SetMeasuredDimensions( GetDefaultSize(flexLayoutWidth, widthMeasureSpec ), GetDefaultSize(flexLayoutHeight, heightMeasureSpec ) ); }
/// <summary> /// Calculate the right measure spec for this child. /// Does the hard part of MeasureChildren: figuring out the MeasureSpec to /// pass to a particular child. This method figures out the right MeasureSpec /// for one dimension (height or width) of one child view.<br /> /// </summary> /// <param name="parentMeasureSpec">The requirements for this view. MeasureSpecification.</param> /// <param name="padding">The padding of this view for the current dimension and margins, if applicable. LayoutLength.</param> /// <param name="childDimension"> How big the child wants to be in the current dimension. LayoutLength.</param> /// <returns>a MeasureSpec for the child.</returns> public static MeasureSpecification GetChildMeasureSpecification(MeasureSpecification parentMeasureSpec, LayoutLength padding, LayoutLength childDimension) { MeasureSpecification.ModeType specMode = parentMeasureSpec.Mode; MeasureSpecification.ModeType resultMode = MeasureSpecification.ModeType.Unspecified; LayoutLength resultSize = new LayoutLength(Math.Max(0.0f, (parentMeasureSpec.Size.AsDecimal() - padding.AsDecimal()))); // reduce available size by the owners padding switch (specMode) { // Parent has imposed an exact size on us case MeasureSpecification.ModeType.Exactly: { if ((int)childDimension.AsRoundedValue() == LayoutParamPolicies.MatchParent) { // Child wants to be our size. So be it. resultMode = MeasureSpecification.ModeType.Exactly; } else if ((int)childDimension.AsRoundedValue() == LayoutParamPolicies.WrapContent) { // Child wants to determine its own size. It can't be // bigger than us. resultMode = MeasureSpecification.ModeType.AtMost; } else { resultSize = childDimension; resultMode = MeasureSpecification.ModeType.Exactly; } break; } // Parent has imposed a maximum size on us case MeasureSpecification.ModeType.AtMost: { if (childDimension.AsRoundedValue() == LayoutParamPolicies.MatchParent) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultMode = MeasureSpecification.ModeType.AtMost; } else if (childDimension.AsRoundedValue() == LayoutParamPolicies.WrapContent) { // Child wants to determine its own size. It can't be // bigger than us. resultMode = MeasureSpecification.ModeType.AtMost; } else { // Child wants a specific size... so be it resultSize = childDimension + padding; resultMode = MeasureSpecification.ModeType.Exactly; } break; } // Parent asked to see how big we want to be case MeasureSpecification.ModeType.Unspecified: { if ((childDimension.AsRoundedValue() == LayoutParamPolicies.MatchParent)) { // Child wants to be our size... find out how big it should be resultMode = MeasureSpecification.ModeType.Unspecified; } else if (childDimension.AsRoundedValue() == (LayoutParamPolicies.WrapContent)) { // Child wants to determine its own size.... find out how big // it should be resultMode = MeasureSpecification.ModeType.Unspecified; } else { // Child wants a specific size... let him have it resultSize = childDimension + padding; resultMode = MeasureSpecification.ModeType.Exactly; } break; } } // switch return(new MeasureSpecification(resultSize, resultMode)); }
/// <summary> /// Calculate the right measure spec for this child. /// Does the hard part of MeasureChildren: figuring out the MeasureSpec to /// pass to a particular child. This method figures out the right MeasureSpec /// for one dimension (height or width) of one child view.<br /> /// </summary> /// <param name="parentMeasureSpec">The requirements for this view. MeasureSpecification.</param> /// <param name="padding">The padding of this view for the current dimension and margins, if applicable. LayoutLength.</param> /// <param name="childDimension"> How big the child wants to be in the current dimension. LayoutLength.</param> /// <returns>a MeasureSpec for the child.</returns> public static MeasureSpecification GetChildMeasureSpecification(MeasureSpecification parentMeasureSpec, LayoutLength padding, LayoutLength childDimension) { MeasureSpecification.ModeType specMode = parentMeasureSpec.Mode; MeasureSpecification.ModeType resultMode = MeasureSpecification.ModeType.Unspecified; // Child only can use parent's size without parent's padding and own margin. LayoutLength resultSize = new LayoutLength(Math.Max(0.0f, (parentMeasureSpec.Size - padding).AsDecimal())); switch (specMode) { // Parent has imposed an exact size on us case MeasureSpecification.ModeType.Exactly: { if ((int)childDimension.AsRoundedValue() == LayoutParamPolicies.MatchParent) { resultMode = MeasureSpecification.ModeType.Exactly; } else if ((int)childDimension.AsRoundedValue() == LayoutParamPolicies.WrapContent) { resultMode = MeasureSpecification.ModeType.AtMost; } else { resultSize = childDimension; resultMode = MeasureSpecification.ModeType.Exactly; } break; } // Parent has imposed a maximum size on us case MeasureSpecification.ModeType.AtMost: { if (childDimension.AsRoundedValue() == LayoutParamPolicies.MatchParent) { // Crashed. Cannot calculate. // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultMode = MeasureSpecification.ModeType.AtMost; } else if (childDimension.AsRoundedValue() == LayoutParamPolicies.WrapContent) { // Child wants to determine its own size. It can't be // bigger than us. // Don't need parent's size. Size of this child will be determined by its children. resultMode = MeasureSpecification.ModeType.AtMost; } else { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpecification.ModeType.Exactly; } break; } // Parent asked to see how big we want to be case MeasureSpecification.ModeType.Unspecified: { if ((childDimension.AsRoundedValue() == LayoutParamPolicies.MatchParent)) { // Child wants to be our size... find out how big it should be // There is no one who has exact size in parent hierarchy. // Cannot calculate. resultMode = MeasureSpecification.ModeType.Unspecified; } else if (childDimension.AsRoundedValue() == (LayoutParamPolicies.WrapContent)) { // Child wants to determine its own size.... find out how big // it should be resultMode = MeasureSpecification.ModeType.Unspecified; } else { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpecification.ModeType.Exactly; } break; } } // switch return(new MeasureSpecification(resultSize, resultMode)); }
/// <summary> /// Measure the layout and its content to determine the measured width and the measured height.<br /> /// </summary> /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param> /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param> /// <since_tizen> 6 </since_tizen> protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL; Extents padding = Owner.Padding; Extents margin = Owner.Margin; Interop.FlexLayout.FlexLayout_SetMargin(swigCPtr, Extents.getCPtr(margin)); Interop.FlexLayout.FlexLayout_SetPadding(swigCPtr, Extents.getCPtr(padding)); float width = FlexUndefined; // Behaves as WrapContent (Flex Auto) float height = FlexUndefined; // Behaves as WrapContent (Flex Auto) if (widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || widthMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost) { width = widthMeasureSpec.Size.AsDecimal(); } if (heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || heightMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost) { height = heightMeasureSpec.Size.AsDecimal(); } // Save measureSpec to measure children. // In other Layout, we can pass it as parameter to measuring child but in FlexLayout we can't // because measurChild function is called by native callback. parentMeasureSpecificationWidth = widthMeasureSpec; parentMeasureSpecificationHeight = heightMeasureSpec; // Assign child properties for (int i = 0; i < LayoutChildren.Count; i++) { LayoutItem layoutItem = LayoutChildren[i]; View Child = layoutItem?.Owner; HandleRef childHandleRef = (HandleRef)Child.GetValue(FlexItemProperty); if (childHandleRef.Handle == IntPtr.Zero || Child == null) { continue; } AlignmentType flexAlignemnt = GetFlexAlignmentSelf(Child); PositionType flexPosition = GetFlexPositionType(Child); float flexAspectRatio = GetFlexAspectRatio(Child); float flexBasis = GetFlexBasis(Child); float flexShrink = GetFlexShrink(Child); float flexGrow = GetFlexGrow(Child); Interop.FlexLayout.FlexLayout_SetFlexAlignmentSelf(childHandleRef, (int)flexAlignemnt); Interop.FlexLayout.FlexLayout_SetFlexPositionType(childHandleRef, (int)flexPosition); Interop.FlexLayout.FlexLayout_SetFlexAspectRatio(childHandleRef, flexAspectRatio); Interop.FlexLayout.FlexLayout_SetFlexBasis(childHandleRef, flexBasis); Interop.FlexLayout.FlexLayout_SetFlexShrink(childHandleRef, flexShrink); Interop.FlexLayout.FlexLayout_SetFlexGrow(childHandleRef, flexGrow); } Interop.FlexLayout.FlexLayout_CalculateLayout(swigCPtr, width, height, isLayoutRtl); LayoutLength flexLayoutWidth = new LayoutLength(Interop.FlexLayout.FlexLayout_GetWidth(swigCPtr)); LayoutLength flexLayoutHeight = new LayoutLength(Interop.FlexLayout.FlexLayout_GetHeight(swigCPtr)); Debug.WriteLineIf(LayoutDebugFlex, "FlexLayout OnMeasure width:" + flexLayoutWidth.AsRoundedValue() + " height:" + flexLayoutHeight.AsRoundedValue()); SetMeasuredDimensions(GetDefaultSize(flexLayoutWidth, widthMeasureSpec), GetDefaultSize(flexLayoutHeight, heightMeasureSpec)); }
} // MeasureHorizontally void MeasureVertical(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { var widthMode = widthMeasureSpec.Mode; var heightMode = heightMeasureSpec.Mode; bool isExactly = (heightMode == MeasureSpecification.ModeType.Exactly); bool matchWidth = false; bool allFillParent = true; float maxWidth = 0.0f; float alternativeMaxWidth = 0.0f; float weightedMaxWidth = 0.0f; float totalWeight = 0.0f; // Reset total length _totalLength = 0.0f; LayoutLength usedExcessSpace = new LayoutLength(0); HeightAndWidthState childState = new HeightAndWidthState(MeasuredSize.StateType.MeasuredSizeOK, MeasuredSize.StateType.MeasuredSizeTooSmall); // measure children, and determine if further resolution is required // 1st phase: // We cycle through all children and measure children with weight 0 (non weighted children) according to their specs // to accumulate total used space in mTotalLength based on measured sizes and margins. // Weighted children are not measured at this phase. // Available space for weighted children will be calculated in the phase 2 based on mTotalLength value. uint index = 0; foreach (LayoutItem childLayout in _children) { LayoutLength childDesiredWidth = new LayoutLength(childLayout.Owner.WidthSpecification); LayoutLength childDesiredHeight = new LayoutLength(childLayout.Owner.HeightSpecification); float childWeight = childLayout.Owner.Weight; Extents childMargin = childLayout.Margin; totalWeight += childWeight; bool useExcessSpace = (childDesiredHeight.AsRoundedValue() == 0) && (childWeight > 0); if (isExactly && useExcessSpace) { LayoutLength totalLength = new LayoutLength(_totalLength); _totalLength = Math.Max(totalLength.AsDecimal(), (totalLength + childMargin.Top + childMargin.Bottom).AsDecimal()); } else { LayoutLength childHeight = new LayoutLength(0); if (useExcessSpace) { // The heightMode is either Unspecified or AtMost, and // this child is only laid out using excess space. Measure // using WrapContent so that we can find out the view's // optimal height. We'll restore the original height of 0 // after measurement. MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(widthMeasureSpec, new LayoutLength(childLayout.Padding.Start + childLayout.Padding.End), childDesiredWidth); MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(heightMeasureSpec, new LayoutLength(childLayout.Padding.Top + childLayout.Padding.Bottom), new LayoutLength(LayoutParamPolicies.WrapContent)); childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec); childHeight = childLayout.MeasuredHeight.Size; usedExcessSpace += childHeight; } else { MeasureChild(childLayout, widthMeasureSpec, heightMeasureSpec); childHeight = childLayout.MeasuredHeight.Size; } float length = (childHeight + childMargin.Top + childMargin.Bottom).AsDecimal(); float cellPadding = CellPadding.Height; // No need to add cell padding to the end of last item. if (index >= _children.Count - 1) { cellPadding = 0.0f; } _totalLength = Math.Max(_totalLength, _totalLength + length + cellPadding); } bool matchWidthLocally = false; if (widthMode != MeasureSpecification.ModeType.Exactly && childDesiredWidth == new LayoutLength(LayoutParamPolicies.MatchParent)) { // Will have to re-measure at least this child when we know exact height. matchWidth = true; matchWidthLocally = true; } float marginWidth = (childLayout.Margin.Start) + (childLayout.Margin.End); LayoutLength childWidth = new LayoutLength(childLayout.MeasuredWidth.Size.AsDecimal() + marginWidth); // was combineMeasuredStates() if (childLayout.MeasuredWidthAndState.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall; } if (childLayout.MeasuredHeightAndState.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall; } maxWidth = (Math.Max(maxWidth, childWidth.AsDecimal())); allFillParent = (allFillParent && (childDesiredWidth == new LayoutLength(LayoutParamPolicies.MatchParent))); float widthforWeight = childWidth.AsDecimal(); if (matchWidthLocally) { widthforWeight = marginWidth; } if (childWeight > 0) { /* * Widths of weighted Views are bogus if we end up remeasuring, so keep them separate. */ weightedMaxWidth = Math.Max(weightedMaxWidth, widthforWeight); } else { alternativeMaxWidth = Math.Max(alternativeMaxWidth, widthforWeight); } } // foreach Extents padding = Padding; _totalLength += padding.Top + padding.Bottom; LayoutLength heightSize = new LayoutLength(_totalLength); heightSize = new LayoutLength(Math.Max(heightSize.AsDecimal(), SuggestedMinimumHeight.AsDecimal())); MeasuredSize heightSizeAndState = ResolveSizeAndState(heightSize, heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK); heightSize = heightSizeAndState.Size; // Either expand children with weight to take up available space or // shrink them if they extend beyond our current bounds. If we skipped // measurement on any children, we need to measure them now. // 2nd phase: // We cycle through weighted children now (children with weight > 0). // The children are measured with exact size equal to their share of the available space based on their weights. // TotalLength is updated to include weighted children measured sizes. float remainingExcess = heightSize.AsDecimal() - _totalLength + usedExcessSpace.AsDecimal(); if (remainingExcess != 0 && totalWeight > 0.0f) { float remainingWeightSum = totalWeight; _totalLength = 0; foreach (LayoutItem childLayout in _children) { int desiredWidth = childLayout.Owner.WidthSpecification; int desiredHeight = childLayout.Owner.HeightSpecification; float childWeight = childLayout.Owner.Weight; Extents childMargin = childLayout.Margin; float childHeight = 0; if (childWeight > 0) { float share = (childWeight * remainingExcess) / remainingWeightSum; remainingExcess -= share; remainingWeightSum -= childWeight; // Always lay out weighted elements with intrinsic size regardless of the parent spec // for consistency between specs. if (desiredHeight == 0) { // This child needs to be laid out from scratch using // only its share of excess space. childHeight = share; } else { // This child had some intrinsic width to which we // need to add its share of excess space. childHeight = childLayout.MeasuredHeight.Size.AsDecimal() + share; } MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(widthMeasureSpec, new LayoutLength(Padding.Start + Padding.End), new LayoutLength(desiredWidth)); MeasureSpecification childHeightMeasureSpec = new MeasureSpecification(new LayoutLength(childHeight), MeasureSpecification.ModeType.Exactly); childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec); // Child may now not fit in vertical dimension. if (childLayout.MeasuredHeightAndState.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall; } } bool matchWidthLocally = false; if (widthMode != MeasureSpecification.ModeType.Exactly && desiredWidth == (int)ChildLayoutData.MatchParent) { // Will have to re-measure at least this child when we know exact height. matchWidth = true; matchWidthLocally = true; } float marginWidth = childMargin.Start + childMargin.End; float childWidth = childLayout.MeasuredWidth.Size.AsDecimal() + marginWidth; maxWidth = Math.Max(maxWidth, childWidth); allFillParent = allFillParent && desiredWidth == (int)ChildLayoutData.MatchParent; alternativeMaxWidth = Math.Max(alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth); childHeight = childLayout.MeasuredHeight.Size.AsDecimal(); float length = childHeight + childMargin.Top + childMargin.Bottom; float cellPadding = CellPadding.Height; // No need to add cell padding to the end of last item. if (index >= _children.Count - 1) { cellPadding = 0.0f; } _totalLength = Math.Max(_totalLength, _totalLength + length + cellPadding); } // foreach // Add in our padding _totalLength += Padding.Top + Padding.Bottom; } else { alternativeMaxWidth = Math.Max(alternativeMaxWidth, weightedMaxWidth); } if (!allFillParent && widthMode != MeasureSpecification.ModeType.Exactly) { maxWidth = alternativeMaxWidth; } maxWidth += padding.Start + padding.End; maxWidth = Math.Max(maxWidth, SuggestedMinimumWidth.AsRoundedValue()); heightSizeAndState.State = childState.heightState; SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(maxWidth), widthMeasureSpec, childState.widthState), heightSizeAndState); if (matchWidth) { ForceUniformWidth(_children.Count, heightMeasureSpec); } } // MeasureVertically
private void MeasureHorizontal(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { var widthMode = widthMeasureSpec.Mode; var heightMode = heightMeasureSpec.Mode; bool isExactly = (widthMode == MeasureSpecification.ModeType.Exactly); bool matchHeight = false; bool allFillParent = true; float maxHeight = 0.0f; float alternativeMaxHeight = 0.0f; float weightedMaxHeight = 0.0f; float totalWeight = 0.0f; // Reset total length _totalLength = 0.0f; LayoutLength usedExcessSpace = new LayoutLength(0); HeightAndWidthState childState = new HeightAndWidthState(MeasuredSize.StateType.MeasuredSizeOK, MeasuredSize.StateType.MeasuredSizeOK); // Measure children, and determine if further resolution is required // 1st phase: // We cycle through all children and measure children with weight 0 (non weighted children) according to their specs // to accumulate total used space in totalLength based on measured sizes and margins. // Weighted children are not measured at this phase. // Available space for weighted children will be calculated in the phase 2 based on totalLength value. foreach (LayoutItem childLayout in _children) { LayoutLength childDesiredWidth = new LayoutLength(childLayout.Owner.WidthSpecification); LayoutLength childDesiredHeight = new LayoutLength(childLayout.Owner.HeightSpecification); float childWeight = childLayout.Owner.Weight; Extents childMargin = childLayout.Margin; totalWeight += childWeight; bool useExcessSpace = (childDesiredWidth.AsRoundedValue() == 0) && (childWeight > 0); if (isExactly && useExcessSpace) { _totalLength += childMargin.Start + childMargin.End; } else { if (useExcessSpace) { // The widthMode is either UNSPECIFIED or AT_MOST, and // this child is only laid out using excess space. Measure // using WRAP_CONTENT so that we can find out the view's // optimal width. MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(widthMeasureSpec, new LayoutLength(childLayout.Padding.Start + childLayout.Padding.End), new LayoutLength(LayoutParamPolicies.WrapContent)); MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(heightMeasureSpec, new LayoutLength(childLayout.Padding.Top + childLayout.Padding.Bottom), childDesiredHeight); childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec); usedExcessSpace += childLayout.MeasuredWidth.Size; } else { MeasureChild(childLayout, widthMeasureSpec, heightMeasureSpec); } LayoutLength childWidth = childLayout.MeasuredWidth.Size; LayoutLength length = childWidth + childMargin.Start + childMargin.End; if (isExactly) { _totalLength += length.AsDecimal(); } else { _totalLength = Math.Max(_totalLength, _totalLength + length.AsDecimal() + CellPadding.Width); } } bool matchHeightLocally = false; if (heightMode != MeasureSpecification.ModeType.Exactly && (int)childDesiredHeight.AsRoundedValue() == LayoutParamPolicies.MatchParent) { // Will have to re-measure at least this child when we know exact height. matchHeight = true; matchHeightLocally = true; } LayoutLength marginHeight = new LayoutLength(childMargin.Top + childMargin.Bottom); LayoutLength childHeight = childLayout.MeasuredHeight.Size + marginHeight; if (childLayout.MeasuredWidthAndState.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall; } if (childLayout.MeasuredHeightAndState.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall; } maxHeight = Math.Max(maxHeight, childHeight.AsDecimal()); allFillParent = (allFillParent && childDesiredHeight.AsRoundedValue() == LayoutParamPolicies.MatchParent); if (childWeight > 0) { /* * Heights of weighted Views are bogus if we end up * remeasuring, so keep them separate. */ weightedMaxHeight = Math.Max(weightedMaxHeight, matchHeightLocally ? marginHeight.AsDecimal() : childHeight.AsDecimal()); } else { alternativeMaxHeight = Math.Max(alternativeMaxHeight, matchHeightLocally ? marginHeight.AsDecimal() : childHeight.AsDecimal()); } } // foreach Extents padding = Padding; _totalLength += padding.Start + padding.End; LayoutLength widthSize = new LayoutLength(_totalLength); widthSize = new LayoutLength(Math.Max(widthSize.AsDecimal(), SuggestedMinimumWidth.AsDecimal())); MeasuredSize widthSizeAndState = ResolveSizeAndState(widthSize, widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK); widthSize = widthSizeAndState.Size; if (!allFillParent && heightMode != MeasureSpecification.ModeType.Exactly) { maxHeight = alternativeMaxHeight; } maxHeight += padding.Top + padding.Bottom; maxHeight = Math.Max(maxHeight, SuggestedMinimumHeight.AsRoundedValue()); widthSizeAndState.State = childState.widthState; SetMeasuredDimensions(widthSizeAndState, ResolveSizeAndState(new LayoutLength(maxHeight), heightMeasureSpec, childState.heightState)); if (matchHeight) { ForceUniformHeight(_children.Count, widthMeasureSpec); } } // MeasureHorizontally