} // 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
} // MeasureHorizontal private 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; float usedExcessSpace = 0.0f; 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. // Weighted children are not measured in this phase. // Available space for weighted children will be calculated in the phase 2 based on _totalLength value. for (int i = 0; i < LayoutChildren.Count; i++) { LayoutItem childLayout = LayoutChildren[i]; int childDesiredWidth = childLayout.Owner.WidthSpecification; float childWeight = childLayout.Owner.Weight; Extents childMargin = childLayout.Margin; totalWeight += childWeight; bool useExcessSpace = (childLayout.Owner.HeightSpecification == 0) && (childWeight > 0); if (isExactly && useExcessSpace) { _totalLength = Math.Max(_totalLength, (_totalLength + childMargin.Top + childMargin.Bottom)); } else { 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( new MeasureSpecification( new LayoutLength(widthMeasureSpec.Size - (childLayout.Margin.Start + childLayout.Margin.End)), widthMeasureSpec.Mode), new LayoutLength(Padding.Start + Padding.End), new LayoutLength(childDesiredWidth)); MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification( new MeasureSpecification( new LayoutLength(heightMeasureSpec.Size - (childLayout.Margin.Top + childLayout.Margin.Bottom)), heightMeasureSpec.Mode), new LayoutLength(Padding.Top + Padding.Bottom), new LayoutLength(LayoutParamPolicies.WrapContent)); childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec); usedExcessSpace += childLayout.MeasuredHeight.Size.AsDecimal(); } else { MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0)); } LayoutLength childHeight = childLayout.MeasuredHeight.Size; LayoutLength length = childHeight + childMargin.Top + childMargin.Bottom; if (isExactly) { _totalLength += length.AsDecimal(); } else { _totalLength = Math.Max(_totalLength, _totalLength + length.AsDecimal() + (i < LayoutChildren.Count - 1 ? CellPadding.Height : 0)); } } bool matchWidthLocally = false; if (widthMode != MeasureSpecification.ModeType.Exactly && childDesiredWidth == 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; float childWidth = childLayout.MeasuredWidth.Size.AsDecimal() + marginWidth; if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall; } if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall; } maxWidth = Math.Max(maxWidth, childWidth); allFillParent = (allFillParent && childDesiredWidth == LayoutParamPolicies.MatchParent); if (childWeight > 0) { // Widths of weighted Views are bogus if we end up remeasuring, so keep them separate. weightedMaxWidth = Math.Max(weightedMaxWidth, matchWidthLocally ? marginWidth : childWidth); } else { alternativeMaxWidth = Math.Max(alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth); } } // foreach float heightSize = _totalLength; heightSize = Math.Max(heightSize, SuggestedMinimumHeight.AsDecimal()); MeasuredSize heightSizeAndState = ResolveSizeAndState(new LayoutLength(heightSize + Padding.Top + Padding.Bottom), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK); heightSize = heightSizeAndState.Size.AsDecimal(); // 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 - _totalLength + usedExcessSpace - (Padding.Top + Padding.Bottom); if (remainingExcess != 0 && totalWeight > 0.0f) { float remainingWeight = totalWeight; maxWidth = 0; _totalLength = 0; int numberOfChildren = LayoutChildren.Count; for (int i = 0; i < numberOfChildren; ++i) { LayoutItem childLayout = LayoutChildren[i]; float desiredChildWidth = childLayout.Owner.WidthSpecification; float childWeight = childLayout.Owner.Weight; Extents childMargin = childLayout.Margin; if (childWeight > 0) { MeasureWeightedChild(childLayout, remainingExcess, remainingWeight, childWeight, widthMeasureSpec, heightMeasureSpec, childState, Orientation.Vertical); } float length = childLayout.MeasuredHeight.Size.AsDecimal() + childMargin.Top + childMargin.Bottom; float cellPadding = i < numberOfChildren - 1 ? CellPadding.Height : 0; if (isExactly) { _totalLength += length; } else { float totalLength = _totalLength; _totalLength = Math.Max(_totalLength, _totalLength + length + cellPadding); } bool matchWidthLocally = (widthMode != MeasureSpecification.ModeType.Exactly) && (desiredChildWidth == LayoutParamPolicies.MatchParent); float marginWidth = childMargin.Start + childMargin.End; float childWidth = childLayout.MeasuredWidth.Size.AsDecimal() + marginWidth; maxWidth = Math.Max(maxWidth, childWidth); alternativeMaxWidth = Math.Max(alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth); allFillParent = (allFillParent && desiredChildWidth == LayoutParamPolicies.MatchParent); } // for loop } else { alternativeMaxWidth = Math.Max(alternativeMaxWidth, weightedMaxWidth); } if (!allFillParent && widthMode != MeasureSpecification.ModeType.Exactly) { maxWidth = alternativeMaxWidth; } maxWidth += (Owner.WidthSpecification == LayoutParamPolicies.WrapContent)?(Padding.Start + Padding.End):0; maxWidth = Math.Max(maxWidth, SuggestedMinimumWidth.AsRoundedValue()); heightSizeAndState.State = childState.heightState; SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(maxWidth + Padding.Top + Padding.Bottom), widthMeasureSpec, childState.widthState), heightSizeAndState); if (matchWidth) { ForceUniformWidth(heightMeasureSpec); } } // MeasureVertical
protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { float totalHeight = 0.0f; float totalWidth = 0.0f; HeightAndWidthState childState = new HeightAndWidthState(MeasuredSize.StateType.MeasuredSizeOK, MeasuredSize.StateType.MeasuredSizeOK); float minPositionX = 0.0f; float minPositionY = 0.0f; float maxPositionX = 0.0f; float maxPositionY = 0.0f; // measure children foreach (LayoutItem childLayout in _children) { if (childLayout != null) { // Get size of child MeasureChild(childLayout, widthMeasureSpec, heightMeasureSpec); float childWidth = childLayout.MeasuredWidth.Size.AsDecimal(); float childHeight = childLayout.MeasuredHeight.Size.AsDecimal(); // Determine the width and height needed by the children using their given position and size. // Children could overlap so find the left most and right most child. Position2D childPosition = childLayout.Owner.Position2D; float childLeft = childPosition.X; float childTop = childPosition.Y; minPositionX = Math.Min(minPositionX, childLeft); maxPositionX = Math.Max(maxPositionX, childLeft + childWidth); // Children could overlap so find the highest and lowest child. minPositionY = Math.Min(minPositionY, childTop); maxPositionY = Math.Max(maxPositionY, childTop + childHeight); // Store current width and height needed to contain all children. totalWidth = maxPositionX - minPositionX; totalHeight = maxPositionY - minPositionY; if (childLayout.MeasuredWidthAndState.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall; } if (childLayout.MeasuredWidthAndState.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall; } } } MeasuredSize widthSizeAndState = ResolveSizeAndState(new LayoutLength(totalWidth), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK); MeasuredSize heightSizeAndState = ResolveSizeAndState(new LayoutLength(totalHeight), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK); totalWidth = widthSizeAndState.Size.AsDecimal(); totalHeight = heightSizeAndState.Size.AsDecimal(); // Ensure layout respects it's given minimum size totalWidth = Math.Max(totalWidth, SuggestedMinimumWidth.AsDecimal()); totalHeight = Math.Max(totalHeight, SuggestedMinimumHeight.AsDecimal()); widthSizeAndState.State = childState.widthState; heightSizeAndState.State = childState.heightState; SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(totalWidth), widthMeasureSpec, childState.widthState), ResolveSizeAndState(new LayoutLength(totalHeight), heightMeasureSpec, childState.heightState)); }
internal virtual void OnMeasureIndependentChildren(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { }
/// <summary> /// Ask all of the children of this view to measure themselves, taking into /// account both the MeasureSpec requirements for this view and its padding.<br /> /// The heavy lifting is done in GetChildMeasureSpec.<br /> /// </summary> /// <param name="widthMeasureSpec">The width requirements for this view.</param> /// <param name="heightMeasureSpec">The height requirements for this view.</param> /// <since_tizen> 6 </since_tizen> protected virtual void MeasureChildren(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { foreach (LayoutItem childLayout in LayoutChildren) { MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0)); } }
/// <summary> /// Ask one of the children of this view to measure itself, taking into /// account both the MeasureSpec requirements for this view and its padding.<br /> /// and margins. The heavy lifting is done in GetChildMeasureSpecification.<br /> /// </summary> /// <param name="child">The child to measure.</param> /// <param name="parentWidthMeasureSpec">The width requirements for this view.</param> /// <param name="widthUsed">Extra space that has been used up by the parent horizontally (possibly by other children of the parent).</param> /// <param name="parentHeightMeasureSpec">The height requirements for this view.</param> /// <param name="heightUsed">Extra space that has been used up by the parent vertically (possibly by other children of the parent).</param> /// <exception cref="ArgumentNullException"> Thrown when child is null. </exception> /// <since_tizen> 6 </since_tizen> protected virtual void MeasureChildWithMargins(LayoutItem child, MeasureSpecification parentWidthMeasureSpec, LayoutLength widthUsed, MeasureSpecification parentHeightMeasureSpec, LayoutLength heightUsed) { if (null == child) { throw new ArgumentNullException(nameof(child)); } View childOwner = child.Owner; Extents margin = childOwner.Margin; MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification( new MeasureSpecification( new LayoutLength(parentWidthMeasureSpec.Size + widthUsed - (margin.Start + margin.End)), parentWidthMeasureSpec.Mode), new LayoutLength(Padding.Start + Padding.End), new LayoutLength(childOwner.WidthSpecification)); MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification( new MeasureSpecification( new LayoutLength(parentHeightMeasureSpec.Size + heightUsed - (margin.Top + margin.Bottom)), parentHeightMeasureSpec.Mode), new LayoutLength(Padding.Top + Padding.Bottom), new LayoutLength(childOwner.HeightSpecification)); child.Measure(childWidthMeasureSpec, childHeightMeasureSpec); }
/// <inheritdoc/> /// <since_tizen> 9 </since_tizen> protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { MeasuredSize.StateType childWidthState = MeasuredSize.StateType.MeasuredSizeOK; MeasuredSize.StateType childHeightState = MeasuredSize.StateType.MeasuredSizeOK; for (int i = 0; i < LayoutChildren.Count; i++) { LayoutItem childLayout = LayoutChildren[i]; if (childLayout != null) { MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0)); if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childWidthState = MeasuredSize.StateType.MeasuredSizeTooSmall; } if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childHeightState = MeasuredSize.StateType.MeasuredSizeTooSmall; } } } (float childrenWidth, float childrenHeight) = CalculateChildrenSize(widthMeasureSpec.Size.AsDecimal(), heightMeasureSpec.Size.AsDecimal()); SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(childrenWidth), widthMeasureSpec, childWidthState), ResolveSizeAndState(new LayoutLength(childrenHeight), heightMeasureSpec, childHeightState)); // There are 2 cases which require to calculate children's MeasuredWidth/Height as follows. // // 1. Text with Ellipsis true // TextLabel and TextField calculate MeasuredWidth/Height to cover their text string if they have WrapContent. // This causes children's Ellipsis cannot be displayed with RelativeLayout. // To resolve the above, RelativeLayout recalculates its children's MeasuredWidth/Height based on the children's space calculated by RelativeLayout APIs. // // 2. FillHorizontal/Vertical true // If children set FillHorizontal/Vertical true, then children's MeasuredWidth/Height are not correctly alculated. // Instead, children's size and position are correctly calculated in OnLayout(). // This causes that the grand children's MeasuredWidth/Height are calculated incorrectly. // To resolve the above, RelativeLayout calculates its children's MeasuredWidth/Height based on the children's geometry calculated by RelativeLayout APIs. // // e.g. // Let parent have RelativeLayout and parent's size be 1920x1080. // Let child have WrapContent with SetFillHorizontal/Vertical true. // Let grand child have MatchParent. // Then, child's size is 1920x1080 but child's MeasuredWidth/Height is 0x0. // Then, grand child's MeasuredWidth/Height is 0x0 and size is 0x0. // // TODO: Not to do duplicate operations in OnLayout() again. bool needClearCache = false; for (int i = 0; i < LayoutChildren.Count; i++) { LayoutItem childLayout = LayoutChildren[i]; if (childLayout != null) { bool ellipsisTextWidth = false; bool ellipsisTextHeight = false; bool needMeasuredWidth = false; bool needMeasuredHeight = false; if (((childLayout.Owner is TextLabel textLabel) && textLabel.Ellipsis) || ((childLayout.Owner is TextField textField) && textField.Ellipsis)) { Geometry horizontalSpace = GetHorizontalSpace(childLayout.Owner); if (!horizontalSpace.Size.Equals(0)) { ellipsisTextWidth = true; needClearCache = true; } Geometry verticalSpace = GetVerticalSpace(childLayout.Owner); if (!verticalSpace.Size.Equals(0)) { ellipsisTextHeight = true; needClearCache = true; } } if (!ellipsisTextWidth && RelativeLayout.GetFillHorizontal(childLayout.Owner)) { needMeasuredWidth = true; needClearCache = true; } if (!ellipsisTextHeight && RelativeLayout.GetFillVertical(childLayout.Owner)) { needMeasuredHeight = true; needClearCache = true; } if ((ellipsisTextWidth == false) && (ellipsisTextHeight == false) && (needMeasuredWidth == false) && (needMeasuredHeight == false)) { continue; } float width = childLayout.MeasuredWidth.Size.AsDecimal(); float height = childLayout.MeasuredHeight.Size.AsDecimal(); if (ellipsisTextWidth) { Geometry horizontalSpace = GetHorizontalSpace(childLayout.Owner); if (!horizontalSpace.Size.Equals(0)) { if ((width > horizontalSpace.Size) || ((width < horizontalSpace.Size) && RelativeLayout.GetFillVertical(childLayout.Owner))) { width = horizontalSpace.Size; } } } else if (needMeasuredWidth) { Geometry horizontalGeometry = GetHorizontalLayout(childLayout.Owner); width = horizontalGeometry.Size; } if (ellipsisTextHeight) { Geometry verticalSpace = GetVerticalSpace(childLayout.Owner); if (!verticalSpace.Size.Equals(0)) { if ((height > verticalSpace.Size) || ((height < verticalSpace.Size) && RelativeLayout.GetFillHorizontal(childLayout.Owner))) { height = verticalSpace.Size; } } } else if (needMeasuredHeight) { Geometry verticalGeometry = GetVerticalLayout(childLayout.Owner); height = verticalGeometry.Size; } // Padding sizes are added because Padding sizes will be subtracted in MeasureChild(). MeasureSpecification childWidthMeasureSpec = new MeasureSpecification(new LayoutLength(width + Padding.Start + Padding.End), MeasureSpecification.ModeType.Exactly); MeasureSpecification childHeightMeasureSpec = new MeasureSpecification(new LayoutLength(height + Padding.Top + Padding.Bottom), MeasureSpecification.ModeType.Exactly); // To calculate the grand children's Measure() with the mode type Exactly, // children's Measure() is called with MatchParent if the children have WrapContent. // // i.e. // If children have Wrapcontent and the grand children have MatchParent, // then grand children's MeasuredWidth/Height do not fill the children // because the grand children's Measure() is called with the mode type AtMost. int origWidthSpecification = childLayout.Owner.WidthSpecification; int origHeightSpecification = childLayout.Owner.HeightSpecification; if (ellipsisTextWidth || needMeasuredWidth) { origWidthSpecification = childLayout.Owner.WidthSpecification; childLayout.Owner.WidthSpecification = LayoutParamPolicies.MatchParent; } if (ellipsisTextHeight || needMeasuredHeight) { origHeightSpecification = childLayout.Owner.HeightSpecification; childLayout.Owner.HeightSpecification = LayoutParamPolicies.MatchParent; } MeasureChildWithMargins(childLayout, childWidthMeasureSpec, new LayoutLength(0), childHeightMeasureSpec, new LayoutLength(0)); if (ellipsisTextWidth || needMeasuredWidth) { childLayout.Owner.WidthSpecification = origWidthSpecification; } if (ellipsisTextHeight || needMeasuredHeight) { childLayout.Owner.HeightSpecification = origHeightSpecification; } } } if (needClearCache) { HorizontalRelativeCache.Clear(); VerticalRelativeCache.Clear(); } }
/// <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)); }
internal override void OnMeasureIndependentChildren(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { foreach (var childLayout in LayoutChildren) { if (!childLayout.SetPositionByLayout) { MeasureChildWithoutPadding(childLayout, widthMeasureSpec, heightMeasureSpec); } } }
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 measure variables _totalLength = 0.0f; float usedExcessSpace = 0.0f; HeightAndWidthState childState = new HeightAndWidthState(MeasuredSize.StateType.MeasuredSizeOK, MeasuredSize.StateType.MeasuredSizeOK); // 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. // Max height of children is stored. foreach (LayoutItem childLayout in LayoutChildren) { int childDesiredHeight = childLayout.Owner.HeightSpecification; float childWeight = childLayout.Owner.Weight; Extents childMargin = childLayout.Margin; totalWeight += childWeight; bool useExcessSpace = (childLayout.Owner.WidthSpecification == 0) && (childWeight > 0); if (isExactly && useExcessSpace) { // Children to be laid out with excess space can be measured later _totalLength += childMargin.Start + childMargin.End; } else { if (useExcessSpace) { // The widthMode 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 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), new LayoutLength(childDesiredHeight)); childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec); usedExcessSpace += childLayout.MeasuredWidth.Size.AsDecimal(); } 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 && childDesiredHeight == LayoutParamPolicies.MatchParent) { // A child has set to MatchParent on it's height. // Will have to re-measure at least this child when we know exact height. matchHeight = true; matchHeightLocally = true; } float marginHeight = childMargin.Top + childMargin.Bottom; float childHeight = childLayout.MeasuredHeight.Size.AsDecimal() + marginHeight; if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall; } if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall; } maxHeight = Math.Max(maxHeight, childHeight); allFillParent = (allFillParent && childDesiredHeight == LayoutParamPolicies.MatchParent); if (childWeight > 0) { // Heights of weighted Views are invalid if we end up remeasuring, so store them separately. weightedMaxHeight = Math.Max(weightedMaxHeight, matchHeightLocally ? marginHeight : childHeight); } else { alternativeMaxHeight = Math.Max(alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight); } } // foreach Extents padding = Padding; _totalLength += padding.Start + padding.End; float widthSize = _totalLength; widthSize = Math.Max(widthSize, SuggestedMinimumWidth.AsDecimal()); MeasuredSize widthSizeAndState = ResolveSizeAndState(new LayoutLength(widthSize), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK); widthSize = widthSizeAndState.Size.AsDecimal(); // 2nd phase: // Expand children with weight to take up available space // 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 = widthSize - _totalLength + usedExcessSpace; if (remainingExcess != 0 && totalWeight > 0) { float remainingWeight = totalWeight; maxHeight = 0; _totalLength = 0; int numberOfChildren = LayoutChildren.Count; for (int i = 0; i < numberOfChildren; ++i) { LayoutItem childLayout = LayoutChildren[i]; float desiredChildHeight = childLayout.Owner.HeightSpecification; float childWeight = childLayout.Owner.Weight; Extents childMargin = childLayout.Margin; if (childWeight > 0) { MeasureWeightedChild(childLayout, remainingExcess, remainingWeight, childWeight, widthMeasureSpec, heightMeasureSpec, childState, Orientation.Horizontal); } float length = childLayout.MeasuredWidth.Size.AsDecimal() + childMargin.Start + childMargin.End; float cellPadding = i < numberOfChildren - 1 ? CellPadding.Width : 0; if (isExactly) { _totalLength += length; } else { float totalLength = _totalLength; _totalLength = Math.Max(_totalLength, _totalLength + length + cellPadding); } bool matchHeightLocally = (heightMode != MeasureSpecification.ModeType.Exactly) && (desiredChildHeight == LayoutParamPolicies.MatchParent); float marginHeight = childMargin.Top + childMargin.Bottom; float childHeight = childLayout.MeasuredHeight.Size.AsDecimal() + marginHeight; maxHeight = Math.Max(maxHeight, childHeight); alternativeMaxHeight = Math.Max(alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight); allFillParent = (allFillParent && desiredChildHeight == LayoutParamPolicies.MatchParent); _totalLength += padding.Start + padding.End; } // for loop } else { // No excess space or no weighted children alternativeMaxHeight = Math.Max(alternativeMaxHeight, weightedMaxHeight); } 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(widthMeasureSpec); } } // MeasureHorizontal
} // MeasureHorizontal private void MeasureVertical(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { var widthMode = widthMeasureSpec.Mode; var heightMode = heightMeasureSpec.Mode; bool isWidthExactly = (widthMode == MeasureSpecification.ModeType.Exactly); bool isHeightExactly = (heightMode == MeasureSpecification.ModeType.Exactly); float maxWidth = 0.0f; float totalWeight = 0.0f; int childrenCount = IterateLayoutChildren().Count(); // Child layout, which wants to match its height to its parent's remaining height, is either following 1 or 2. // 1. Child layout whose Owner.HeightSpecification is LayoutParamPolicies.MatchParent. // 2. Child layout whose Owner.HeightSpecification is 0 and Owner.Weight is greater than 0. // The number of child layout which wants to match its height to its parent's remaining height. int childrenMatchParentCount = 0; // Reset measure variable totalLength = 0.0f; HeightAndWidthState childState = new HeightAndWidthState(MeasuredSize.StateType.MeasuredSizeOK, MeasuredSize.StateType.MeasuredSizeOK); // 1ST PHASE: // // We measure all children whose height specification policy is WrapContent without weight. // After 1st phase, remaining height of parent is accumulated to calculate height of children // whose height specification policy is MatchParent. foreach (LayoutItem childLayout in IterateLayoutChildren()) { int childDesiredWidth = childLayout.Owner.WidthSpecification; int childDesiredHeight = childLayout.Owner.HeightSpecification; float childWeight = childLayout.Owner.Weight; Extents childMargin = childLayout.Margin; float childMarginWidth = childMargin.Start + childMargin.End; float childMarginHeight = childMargin.Top + childMargin.Bottom; bool useRemainingHeight = (childDesiredHeight == 0) && (childWeight > 0); totalWeight += childWeight; if ((childDesiredHeight == LayoutParamPolicies.MatchParent) || (useRemainingHeight)) { childrenMatchParentCount++; } // MatchParent child layout's margin is not added to totalLength. // Consequently, MatchParent child layout's margin is added to remaining size, // so the margin is not shared with other MatchParent child layouts. // e.g. // LinearLayout has size 100. // Child layout1 is MatchParent and its margin is 20. (This margin is not ad // Child layout2 is MatchParent and its margin is 0. // Then, child layout1's size is 30 and child layout2's size is 50. if ((childDesiredHeight == LayoutParamPolicies.WrapContent) || ((childDesiredHeight > 0) && (!useRemainingHeight))) { MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0)); float childMeasuredHeight = childLayout.MeasuredHeight.Size.AsDecimal(); if (childMeasuredHeight < 0) { totalLength = Math.Max(totalLength, totalLength + childMarginHeight); } else { totalLength = Math.Max(totalLength, totalLength + childMeasuredHeight + childMarginHeight); } } if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall; } if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall; } float childMeasuredWidth = childLayout.MeasuredWidth.Size.AsDecimal(); if (childMeasuredWidth < 0) { maxWidth = Math.Max(maxWidth, childMarginWidth); } else { maxWidth = Math.Max(maxWidth, childMeasuredWidth + childMarginWidth); } } // 1ST PHASE foreach totalLength = Math.Max(totalLength, totalLength + CellPadding.Height * (childrenCount - 1) + Padding.Top + Padding.Bottom); float heightSize = Math.Max(totalLength, SuggestedMinimumHeight.AsDecimal()); MeasuredSize heightSizeAndState = ResolveSizeAndState(new LayoutLength(heightSize), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK); heightSize = heightSizeAndState.Size.AsDecimal(); float remainingHeight = heightSize - totalLength; float totalWeightLength = 0.0f; // 2ND PHASE: // // We measure all children whose height specification policy is MatchParent without weight. // After 2nd phase, all children's heights are calculated without considering weight. // And the heights of all weighted children are accumulated to calculate weighted height. foreach (LayoutItem childLayout in IterateLayoutChildren()) { int childDesiredWidth = childLayout.Owner.WidthSpecification; int childDesiredHeight = childLayout.Owner.HeightSpecification; float childWeight = childLayout.Owner.Weight; Extents childMargin = childLayout.Margin; bool useRemainingHeight = (childDesiredHeight == 0) && (childWeight > 0); bool needToMeasure = false; if ((childDesiredWidth == LayoutParamPolicies.MatchParent) || (useRemainingHeight)) { if (isWidthExactly) { needToMeasure = true; } // RelativeLayout's MatchParent children should not fill to the RelativeLayout. // Because the children's sizes and positions are calculated by RelativeLayout's APIs. // Therefore, not to fill the RelativeLayout, the mode is changed from Exactly to AtMost. // // Not to print the recursive reference error message for this case, Specification is checked if it is WrapContent. else if (Owner.WidthSpecification == LayoutParamPolicies.WrapContent) { if (childDesiredWidth == LayoutParamPolicies.MatchParent) { Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s WidthSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s WidthSpecification is MatchParent!"); } else { Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s WidthSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s WidthSpecification is 0 with positive weight!"); } } } if (remainingHeight > 0) { if ((childDesiredHeight == LayoutParamPolicies.MatchParent) || (useRemainingHeight)) { if (isHeightExactly) { // In MeasureChildWithMargins(), it is assumed that heightMeasureSpec includes Padding.Top and Padding.Bottom. // Therefore, Padding.Top and Padding.Bottom are added to heightMeasureSpec.Size before it is passed to MeasureChildWithMargins(). heightMeasureSpec.SetSize(new LayoutLength((int)(remainingHeight / childrenMatchParentCount) + Padding.Top + Padding.Bottom)); needToMeasure = true; } // RelativeLayout's MatchParent children should not fill to the RelativeLayout. // Because the children's sizes and positions are calculated by RelativeLayout's APIs. // Therefore, not to fill the RelativeLayout, the mode is changed from Exactly to AtMost. // // Not to print the recursive reference error message for this case, Specification is checked if it is WrapContent. else if (Owner.HeightSpecification == LayoutParamPolicies.WrapContent) { if (childDesiredHeight == LayoutParamPolicies.MatchParent) { Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s HeightSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s HeightSpecification is MatchParent!"); } else { Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s HeightSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s HeightSpecification is 0 with positive weight!"); } } } } if (needToMeasure == true) { MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0)); } if (childWeight > 0) { float childMeasuredHeight = childLayout.MeasuredHeight.Size.AsDecimal(); if (childMeasuredHeight < 0) { totalWeightLength = Math.Max(totalWeightLength, totalWeightLength + childMargin.Top + childMargin.Bottom); } else { totalWeightLength = Math.Max(totalWeightLength, totalWeightLength + childMeasuredHeight + childMargin.Top + childMargin.Bottom); } } } // 2ND PHASE foreach // 3RD PHASE: // // We measure all weighted children whose owner has weight greater than 0. // After 3rd phase, all weighted children has height which is proportional to their weights // in remaining height of parent. if (totalWeight > 0) { foreach (LayoutItem childLayout in IterateLayoutChildren()) { int childDesiredHeight = childLayout.Owner.HeightSpecification; float childWeight = childLayout.Owner.Weight; if ((childWeight > 0) && ((childDesiredHeight == LayoutParamPolicies.MatchParent) || (childDesiredHeight == 0))) { if (isHeightExactly) { MeasureWeightedChild(childLayout, totalWeightLength, totalWeight, childWeight, widthMeasureSpec, heightMeasureSpec, childState, Orientation.Vertical); } // RelativeLayout's MatchParent children should not fill to the RelativeLayout. // Because the children's sizes and positions are calculated by RelativeLayout's APIs. // Therefore, not to fill the RelativeLayout, the mode is changed from Exactly to AtMost. // // Not to print the recursive reference error message for this case, Specification is checked if it is WrapContent. else if (Owner.HeightSpecification == LayoutParamPolicies.WrapContent) { if (childDesiredHeight == LayoutParamPolicies.MatchParent) { Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s HeightSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s HeightSpecification is MatchParent!"); } else { Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s HeightSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s HeightSpecification is 0 with positive weight!"); } } } } // 3RD PHASE foreach } maxWidth = Math.Max(maxWidth, maxWidth + (Padding.Start + Padding.End)); maxWidth = Math.Max(maxWidth, SuggestedMinimumWidth.AsRoundedValue()); heightSizeAndState.State = childState.heightState; SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(maxWidth), widthMeasureSpec, childState.widthState), heightSizeAndState); } // MeasureVertical
/// <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)); }
/// <inheritdoc/> /// <since_tizen> 9 </since_tizen> protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { MeasuredSize.StateType childWidthState = MeasuredSize.StateType.MeasuredSizeOK; MeasuredSize.StateType childHeightState = MeasuredSize.StateType.MeasuredSizeOK; for (int i = 0; i < LayoutChildren.Count; i++) { LayoutItem childLayout = LayoutChildren[i]; if (childLayout != null) { MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0)); if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childWidthState = MeasuredSize.StateType.MeasuredSizeTooSmall; } if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childHeightState = MeasuredSize.StateType.MeasuredSizeTooSmall; } } } (float childrenWidth, float childrenHeight) = CalculateChildrenSize(widthMeasureSpec.Size.AsDecimal(), heightMeasureSpec.Size.AsDecimal()); SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(childrenWidth), widthMeasureSpec, childWidthState), ResolveSizeAndState(new LayoutLength(childrenHeight), heightMeasureSpec, childHeightState)); // RelativeLayout sets its children's size in OnLayout(). // Therefore, the children's MeasuredWidth/Height are not the same with the children's size. // This causes that the grand children's MeasuredWidth/Height are calculated incorrectly. // To resolve the above, RelativeLayout updates its children's MeasuredWidth/Height after // the RelativeLayout's MeasuredWidth/Height are calculated. // // e.g. // Let parent have RelativeLayout and parent's size be 1920x1080. // Let child have WrapContent with SetFillHorizontal/Vertical true. // Let grand child have MatchParent. // Then, child's size is 1920x1080 but child's MeasuredWidth/Height is 0x0. // Then, grand child's MeasuredWidth/Height is 0x0 and size is 0x0. // // TODO: Not to do the following operation in OnLayout() again. for (int i = 0; i < LayoutChildren.Count; i++) { LayoutItem childLayout = LayoutChildren[i]; if (childLayout != null) { LayoutLength childLeft; LayoutLength childRight; LayoutLength childTop; LayoutLength childBottom; if (childLayout.Owner.WidthSpecification == LayoutParamPolicies.MatchParent) { childLeft = new LayoutLength(childLayout.Margin.Start); childRight = new LayoutLength(MeasuredWidth.Size.AsDecimal() - childLayout.Margin.End); } else { Geometry horizontalGeometry = GetHorizontalLayout(childLayout.Owner); childLeft = new LayoutLength(horizontalGeometry.Position + Padding.Start + childLayout.Margin.Start); childRight = new LayoutLength(horizontalGeometry.Position + horizontalGeometry.Size + Padding.Start - childLayout.Margin.End); } if (childLayout.Owner.HeightSpecification == LayoutParamPolicies.MatchParent) { childTop = new LayoutLength(childLayout.Margin.Top); childBottom = new LayoutLength(MeasuredHeight.Size.AsDecimal() - childLayout.Margin.Bottom); } else { Geometry verticalGeometry = GetVerticalLayout(childLayout.Owner); childTop = new LayoutLength(verticalGeometry.Position + Padding.Top + childLayout.Margin.Top); childBottom = new LayoutLength(verticalGeometry.Position + verticalGeometry.Size + Padding.Top - childLayout.Margin.Bottom); } // Padding sizes are added because Padding sizes will be subtracted in MeasureChild(). MeasureSpecification childWidthMeasureSpec = new MeasureSpecification(new LayoutLength(childRight.AsDecimal() - childLeft.AsDecimal() + Padding.Start + Padding.End), MeasureSpecification.ModeType.Exactly); MeasureSpecification childHeightMeasureSpec = new MeasureSpecification(new LayoutLength(childBottom.AsDecimal() - childTop.AsDecimal() + Padding.Top + Padding.Bottom), MeasureSpecification.ModeType.Exactly); int origWidthSpecification = childLayout.Owner.WidthSpecification; int origHeightSpecification = childLayout.Owner.HeightSpecification; // To calculate the grand children's Measure() with the mode type Exactly, // children's Measure() is called with MatchParent if the children have WrapContent. // // i.e. // If children have Wrapcontent and the grand children have MatchParent, // then grand children's MeasuredWidth/Height do not fill the children // because the grand children's Measure() is called with the mode type AtMost. if (childLayout.Owner.WidthSpecification == LayoutParamPolicies.WrapContent) { childLayout.Owner.WidthSpecification = LayoutParamPolicies.MatchParent; } if (childLayout.Owner.HeightSpecification == LayoutParamPolicies.WrapContent) { childLayout.Owner.HeightSpecification = LayoutParamPolicies.MatchParent; } // Use MeasureChild() because Margin sizes were already subtracted from childWidth/HeightMeasureSpec. MeasureChild(childLayout, childWidthMeasureSpec, childHeightMeasureSpec); childLayout.Owner.WidthSpecification = origWidthSpecification; childLayout.Owner.HeightSpecification = origHeightSpecification; } } }
/// <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) { var gridWidthMode = widthMeasureSpec.Mode; var gridHeightMode = heightMeasureSpec.Mode; int widthSize = (int)widthMeasureSpec.Size.AsRoundedValue(); int heightSize = (int)heightMeasureSpec.Size.AsRoundedValue(); int availableContentWidth; int availableContentHeight; int desiredChildHeight; int desiredChildWidth; Extents gridLayoutPadding = Padding; var childCount = LayoutChildren.Count; // WIDTH SPECIFICATIONS // measure first child and use it's dimensions for layout measurement if (childCount > 0) { LayoutItem childLayoutItem = LayoutChildren[0]; View childOwner = childLayoutItem.Owner; MeasureChild(childLayoutItem, widthMeasureSpec, heightMeasureSpec); desiredChildHeight = (int)childLayoutItem.MeasuredHeight.Size.AsRoundedValue(); desiredChildWidth = (int)childLayoutItem.MeasuredWidth.Size.AsRoundedValue(); // If child has a margin then add it to desired size Extents childMargin = childLayoutItem.Margin; desiredChildHeight += childMargin.Top + childMargin.Bottom; desiredChildWidth += childMargin.Start + childMargin.End; _totalWidth = desiredChildWidth * _columns; // Include padding for max and min checks _totalWidth += gridLayoutPadding.Start + gridLayoutPadding.End; // Ensure width does not exceed specified at most width or less than mininum width _totalWidth = Math.Max(_totalWidth, (int)SuggestedMinimumWidth.AsRoundedValue()); // widthMode EXACTLY so grid must be the given width if (gridWidthMode == MeasureSpecification.ModeType.Exactly || gridWidthMode == MeasureSpecification.ModeType.AtMost) { // In the case of AT_MOST, widthSize is the max limit. _totalWidth = Math.Min(_totalWidth, widthSize); } availableContentWidth = _totalWidth - gridLayoutPadding.Start - gridLayoutPadding.End; widthSize = _totalWidth; // HEIGHT SPECIFICATIONS // heightMode EXACTLY so grid must be the given height if (gridHeightMode == MeasureSpecification.ModeType.Exactly || gridHeightMode == MeasureSpecification.ModeType.AtMost) { if (childCount > 0) { _totalHeight = gridLayoutPadding.Top + gridLayoutPadding.Bottom; for (int i = 0; i < childCount; i += _columns) { _totalHeight += desiredChildHeight; } // Ensure ourHeight does not exceed specified at most height _totalHeight = Math.Min(_totalHeight, heightSize); _totalHeight = Math.Max(_totalHeight, (int)SuggestedMinimumHeight.AsRoundedValue()); heightSize = _totalHeight; } // Child exists // In the case of AT_MOST, availableContentHeight is the max limit. availableContentHeight = heightSize - gridLayoutPadding.Top - gridLayoutPadding.Bottom; } else { // Grid expands to fit content // If number of columns AUTO_FIT then set to 1 column. _columns = (_columns > 0) ? _columns : 1; // Calculate numbers of rows, round down result as later check for remainder. _rows = childCount / _columns; // If number of cells not cleanly dividable by columns, add another row to house remainder cells. _rows += (childCount % _columns > 0) ? 1 : 0; availableContentHeight = desiredChildHeight * _rows; } // If number of columns not defined DetermineNumberOfColumns(availableContentWidth); // Locations define the start, end,top and bottom of each cell. _locations.CalculateLocations(_columns, availableContentWidth, availableContentHeight, childCount); } // Children exists SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(widthSize), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK), ResolveSizeAndState(new LayoutLength(heightSize), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK)); }
/// <summary> /// Ask one of the children of this view to measure itself, taking into /// account both the MeasureSpec requirements for this view and its padding.<br /> /// and margins. The child must have MarginLayoutParams The heavy lifting is /// done in GetChildMeasureSpecification.<br /> /// </summary> /// <param name="child">The child to measure.</param> /// <param name="parentWidthMeasureSpec">The width requirements for this view.</param> /// <param name="widthUsed">Extra space that has been used up by the parent horizontally (possibly by other children of the parent).</param> /// <param name="parentHeightMeasureSpec">The height requirements for this view.</param> /// <param name="heightUsed">Extra space that has been used up by the parent vertically (possibly by other children of the parent).</param> /// <since_tizen> 6 </since_tizen> protected virtual void MeasureChildWithMargins(LayoutItem child, MeasureSpecification parentWidthMeasureSpec, LayoutLength widthUsed, MeasureSpecification parentHeightMeasureSpec, LayoutLength heightUsed) { View childOwner = child.Owner; MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification( new MeasureSpecification( new LayoutLength(parentWidthMeasureSpec.Size + widthUsed - (Padding.Start + Padding.End + childOwner.Margin.Start + childOwner.Margin.End)), parentWidthMeasureSpec.Mode), new LayoutLength(Padding.Start + Padding.End), new LayoutLength(childOwner.WidthSpecification)); MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification( new MeasureSpecification( new LayoutLength(parentHeightMeasureSpec.Size + heightUsed - (Padding.Top + Padding.Bottom + childOwner.Margin.Top + childOwner.Margin.Bottom)), parentHeightMeasureSpec.Mode), new LayoutLength(Padding.Top + Padding.Bottom), new LayoutLength(childOwner.HeightSpecification)); child.Measure(childWidthMeasureSpec, childHeightMeasureSpec); }
// Starts of the actual measuring and layouting from the given root node. // Can be called from multiple starting roots but in series. void MeasureAndLayout(View root) { if (root != null) { // Get parent MeasureSpecification, this could be the Window or View with an exact size. Container parentNode = root.GetParent(); Size2D rootSize; Position2D rootPosition = root.Position2D; View parentView = parentNode as View; if (parentView) { // Get parent View's Size. If using Legacy size negotiation then should have been set already. rootSize = new Size2D(parentView.Size2D.Width, parentView.Size2D.Height); } else { // Parent not a View so assume it's a Layer which is the size of the window. rootSize = new Size2D(_window.Size.Width, _window.Size.Height); } // Determine measure specification for root. // The root layout policy could be an exact size, be match parent or wrap children. // If wrap children then at most it can be the root parent size. // If match parent then should be root parent size. // If exact then should be that size limited by the root parent size. LayoutLength width = new LayoutLength(rootSize.Width); LayoutLength height = new LayoutLength(rootSize.Height); MeasureSpecification.ModeType widthMode = MeasureSpecification.ModeType.AtMost; MeasureSpecification.ModeType heightMode = MeasureSpecification.ModeType.AtMost; if (root.WidthSpecification >= 0) { // exact size provided so match width exactly width = new LayoutLength(root.WidthSpecification); widthMode = MeasureSpecification.ModeType.Exactly; } else if (root.WidthSpecification == LayoutParamPolicies.MatchParent) { widthMode = MeasureSpecification.ModeType.Exactly; } if (root.HeightSpecification >= 0) { // exact size provided so match height exactly height = new LayoutLength(root.HeightSpecification); heightMode = MeasureSpecification.ModeType.Exactly; } else if (root.HeightSpecification == LayoutParamPolicies.MatchParent) { heightMode = MeasureSpecification.ModeType.Exactly; } MeasureSpecification parentWidthSpecification = new MeasureSpecification(width, widthMode); MeasureSpecification parentHeightSpecification = new MeasureSpecification(height, heightMode); // Start at root with it's parent's widthSpecification and heightSpecification MeasureHierarchy(root, parentWidthSpecification, parentHeightSpecification); // Start at root which was just measured. PerformLayout(root, new LayoutLength(rootPosition.X), new LayoutLength(rootPosition.Y), new LayoutLength(rootPosition.X) + root.Layout.MeasuredWidth.Size, new LayoutLength(rootPosition.Y) + root.Layout.MeasuredHeight.Size); bool readyToPlay = SetupCoreAnimation(); if (readyToPlay && OverrideCoreAnimation == false) { PlayAnimation(); } } }
/// <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)); }
internal override void OnMeasureIndependentChildren(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { foreach (LayoutItem childLayout in LayoutChildren.Where(item => item?.Owner?.ExcludeLayouting ?? false)) { MeasureChildWithoutPadding(childLayout, widthMeasureSpec, heightMeasureSpec); } }
/// <summary> /// Ask all of the children of this view to measure themselves, taking into /// account both the MeasureSpec requirements for this view and its padding.<br /> /// The heavy lifting is done in GetChildMeasureSpec.<br /> /// </summary> /// <param name="widthMeasureSpec">The width requirements for this view.</param> /// <param name="heightMeasureSpec">The height requirements for this view.</param> protected virtual void MeasureChildren(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { foreach (LayoutItem childLayout in _children) { MeasureChild(childLayout, widthMeasureSpec, heightMeasureSpec); } }
/// <summary> /// Ask one of the children of this view to measure itself, taking into /// account both the MeasureSpec requirements for this view and its padding.<br /> /// The heavy lifting is done in GetChildMeasureSpecification.<br /> /// </summary> /// <param name="child">The child to measure.</param> /// <param name="parentWidthMeasureSpec">The width requirements for this view.</param> /// <param name="parentHeightMeasureSpec">The height requirements for this view.</param> /// <exception cref="ArgumentNullException"> Thrown when child is null. </exception> /// <since_tizen> 6 </since_tizen> protected virtual void MeasureChild(LayoutItem child, MeasureSpecification parentWidthMeasureSpec, MeasureSpecification parentHeightMeasureSpec) { if (null == child) { throw new ArgumentNullException(nameof(child)); } View childOwner = child.Owner; MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification( new MeasureSpecification(new LayoutLength(parentWidthMeasureSpec.Size), parentWidthMeasureSpec.Mode), new LayoutLength(Padding.Start + Padding.End), new LayoutLength(childOwner.WidthSpecification)); MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification( new MeasureSpecification(new LayoutLength(parentHeightMeasureSpec.Size), parentHeightMeasureSpec.Mode), new LayoutLength(Padding.Top + Padding.Bottom), new LayoutLength(childOwner.HeightSpecification)); child.Measure(childWidthMeasureSpec, childHeightMeasureSpec); }
/// <summary> /// Ask one of the children of this view to measure itself, taking into /// account both the MeasureSpec requirements for this view and its padding.<br /> /// The heavy lifting is done in GetChildMeasureSpec.<br /> /// </summary> /// <param name="child">The child to measure.</param> /// <param name="parentWidthMeasureSpec">The width requirements for this view.</param> /// <param name="parentHeightMeasureSpec">The height requirements for this view.</param> protected virtual void MeasureChild(LayoutItem child, MeasureSpecification parentWidthMeasureSpec, MeasureSpecification parentHeightMeasureSpec) { View childOwner = child.Owner; Extents padding = child.Padding; // Padding of this layout's owner, not of the child being measured. MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(parentWidthMeasureSpec, new LayoutLength(padding.Start + padding.End), new LayoutLength(childOwner.WidthSpecification)); MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(parentHeightMeasureSpec, new LayoutLength(padding.Top + padding.Bottom), new LayoutLength(childOwner.HeightSpecification)); child.Measure(childWidthMeasureSpec, childHeightMeasureSpec); }
protected void MeasureChildWithoutPadding(LayoutItem child, MeasureSpecification parentWidthMeasureSpec, MeasureSpecification parentHeightMeasureSpec) { View childOwner = child.Owner; MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification( new MeasureSpecification(new LayoutLength(parentWidthMeasureSpec.Size), parentWidthMeasureSpec.Mode), new LayoutLength(0), new LayoutLength(childOwner.WidthSpecification)); MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification( new MeasureSpecification(new LayoutLength(parentHeightMeasureSpec.Size), parentHeightMeasureSpec.Mode), new LayoutLength(0), new LayoutLength(childOwner.HeightSpecification)); child.Measure(childWidthMeasureSpec, childHeightMeasureSpec); }
/// <summary> /// Ask one of the children of this view to measure itself, taking into /// account both the MeasureSpec requirements for this view and its padding.<br /> /// and margins. The child must have MarginLayoutParams The heavy lifting is /// done in GetChildMeasureSpecification.<br /> /// </summary> /// <param name="child">The child to measure.</param> /// <param name="parentWidthMeasureSpec">The width requirements for this view.</param> /// <param name="widthUsed">Extra space that has been used up by the parent horizontally (possibly by other children of the parent).</param> /// <param name="parentHeightMeasureSpec">The height requirements for this view.</param> /// <param name="heightUsed">Extra space that has been used up by the parent vertically (possibly by other children of the parent).</param> protected virtual void MeasureChildWithMargins(LayoutItem child, MeasureSpecification parentWidthMeasureSpec, LayoutLength widthUsed, MeasureSpecification parentHeightMeasureSpec, LayoutLength heightUsed) { View childOwner = child.Owner; int desiredWidth = childOwner.WidthSpecification; int desiredHeight = childOwner.HeightSpecification; Extents padding = child.Padding; // Padding of this layout's owner, not of the child being measured. MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(parentWidthMeasureSpec, new LayoutLength(padding.Start + padding.End) + widthUsed, new LayoutLength(desiredWidth)); MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(parentHeightMeasureSpec, new LayoutLength(padding.Top + padding.Bottom) + heightUsed, new LayoutLength(desiredHeight)); child.Measure(childWidthMeasureSpec, childHeightMeasureSpec); }
/// <summary> /// Measure the layout and its content to determine the measured width and the /// measured height.<br /> /// The base class implementation of measure defaults to the background size, /// unless a larger size is allowed by the MeasureSpec. Subclasses should /// override to provide better measurements of their content.<br /> /// If this method is overridden, it is the subclass's responsibility to make sure the /// measured height and width are at least the layout's minimum height and width.<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 virtual void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { // GetDefaultSize will limit the MeasureSpec to the suggested minimumWidth and minimumHeight SetMeasuredDimensions(GetDefaultSize(SuggestedMinimumWidth, widthMeasureSpec), GetDefaultSize(SuggestedMinimumHeight, heightMeasureSpec)); }
private void MeasureWeightedChild(LayoutItem childLayout, float remainingExcess, float remainingWeight, float childWeight, MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec, HeightAndWidthState childState, Orientation orientation) { bool horizontal = false; if (orientation == Orientation.Horizontal) { horizontal = true; } float childsShare = (childWeight * remainingExcess) / remainingWeight; remainingExcess -= childsShare; remainingWeight -= childWeight; float desiredWidth = childLayout.Owner.WidthSpecification; float desiredHeight = childLayout.Owner.HeightSpecification; float childLength = 0; // Always lay out weighted elements with intrinsic size regardless of the parent spec. // for consistency between specs. if ((horizontal && (desiredWidth == 0)) || (!horizontal && (desiredHeight == 0))) { // This child needs to be laid out from scratch using // only its share of excess space. childLength = childsShare; } else { // This child had some intrinsic width to which we // need to add its share of excess space. if (horizontal) { childLength = childLayout.MeasuredWidth.Size.AsDecimal() + childsShare; } else { childLength = childLayout.MeasuredHeight.Size.AsDecimal() + childsShare; } } MeasureSpecification childWidthMeasureSpec; MeasureSpecification childHeightMeasureSpec; if (horizontal) { childWidthMeasureSpec = new MeasureSpecification(new LayoutLength(childLength), MeasureSpecification.ModeType.Exactly); childHeightMeasureSpec = GetChildMeasureSpecification( new MeasureSpecification( new LayoutLength(heightMeasureSpec.Size - (childLayout.Owner.Margin.Top + childLayout.Owner.Margin.Bottom)), heightMeasureSpec.Mode), new LayoutLength(Padding.Top + Padding.Bottom), new LayoutLength(desiredHeight)); } else // vertical { childWidthMeasureSpec = GetChildMeasureSpecification( new MeasureSpecification( new LayoutLength(widthMeasureSpec.Size - (childLayout.Owner.Margin.Start + childLayout.Owner.Margin.End)), widthMeasureSpec.Mode), new LayoutLength(Padding.Start + Padding.End), new LayoutLength(desiredWidth)); childHeightMeasureSpec = new MeasureSpecification(new LayoutLength(childLength), MeasureSpecification.ModeType.Exactly); } childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec); // Child may now not fit in horizontal dimension. if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall; } // Child may now not fit in vertical dimension. if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall; } }
private void InitChildrenData(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { bool isHorizontal = (GridOrientation == Orientation.Horizontal); int mainPivot = 0, subPivot = 0; int[] pivotStack = new int[isHorizontal ? Columns : Rows]; vLocations = hLocations = null; vEdgeList = hEdgeList = null; gridChildren = new List <GridChild>(); maxColumnConut = Columns; maxRowCount = Rows; totalVerticalExpand = 0; totalHorizontalExpand = 0; foreach (LayoutItem item in IterateLayoutChildren()) { int column, columnSpan, row, rowSpan; StretchFlags verticalStretch, horizontalStretch; View view = item.Owner; column = GetColumn(view); columnSpan = GetColumnSpan(view); row = GetRow(view); rowSpan = GetRowSpan(view); verticalStretch = GetVerticalStretch(view); horizontalStretch = GetHorizontalStretch(view); if (column + columnSpan > maxColumnConut || row + rowSpan > maxRowCount) { if (column + columnSpan > maxColumnConut) { Tizen.Log.Error("NUI", "Column + ColumnSapn exceeds Grid Columns. Column + ColumnSpan (" + column + " + " + columnSpan + ") > Grid Columns(" + maxColumnConut + ")"); } else { Tizen.Log.Error("NUI", "Row + RowSapn exceeds Grid Rows. Row + RowSapn (" + row + " + " + rowSpan + ") > Grid Rows(" + maxRowCount + ")"); } gridChildren.Add(new GridChild(null, new Node(0, 1, 0, 0), new Node(0, 1, 0, 0))); continue; } if (horizontalStretch.HasFlag(StretchFlags.Expand)) { totalHorizontalExpand++; } if (verticalStretch.HasFlag(StretchFlags.Expand)) { totalVerticalExpand++; } // assign column/row depending on GridOrientation. The main axis count(Columns on Horizontal, Rows otherwise) won't be exceeded // explicit column(row) count which is assigned by Columns(Rows). but, cross axis count(Rows(Columns)) can be increased by sub axis count. if (column == AutoColumn || row == AutoRow) { (int point, int span)mainAxis = isHorizontal ? (column, columnSpan) : (row, rowSpan); (int point, int span)subAxis = isHorizontal ? (row, rowSpan) : (column, columnSpan); if (subAxis.point != AutoColumn && subAxis.point != AutoRow) { subPivot = subAxis.point; } if (mainAxis.point != AutoColumn && mainAxis.point != AutoRow) { mainPivot = mainAxis.point; } if (mainPivot + mainAxis.span > pivotStack.Length) { mainPivot = 0; subPivot++; } for (int n = mainPivot + mainAxis.span - 1; n >= mainPivot; n--) { if (pivotStack[n] > subPivot) { mainPivot = n + 1; n = mainPivot + mainAxis.span; if (n > pivotStack.Length) { if (mainAxis.point != AutoColumn && mainAxis.point != AutoRow) { mainPivot = mainAxis.point; } else { mainPivot = 0; } n = mainPivot + mainAxis.span; subPivot++; } } } if (isHorizontal) { column = mainPivot; row = subPivot; } else { column = subPivot; row = mainPivot; } for (int start = mainPivot, end = mainPivot + mainAxis.span; start < end; start++) { pivotStack[start] = subPivot + subAxis.span; } mainPivot += mainAxis.span; } if (maxColumnConut < column + columnSpan) { maxColumnConut = column + columnSpan; } if (maxRowCount < row + rowSpan) { maxRowCount = row + rowSpan; } MeasureChildWithMargins(item, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0)); gridChildren.Add(new GridChild(item, new Node(column, columnSpan, item.MeasuredWidth.Size.AsDecimal() + item.Owner.Margin.Start + item.Owner.Margin.End, horizontalStretch), new Node(row, rowSpan, item.MeasuredHeight.Size.AsDecimal() + item.Owner.Margin.Top + item.Owner.Margin.Bottom, verticalStretch))); } }
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