public void SetStackAreaAxis(LayoutAxis stackAreaAxis) { if (this.stackAreaAxis != stackAreaAxis) { this.stackAreaAxis = stackAreaAxis; this.Reposition(); } }
public void SetLayoutAxis(LayoutAxis layoutAxis) { if (this.layoutAxis != layoutAxis) { this.layoutAxis = layoutAxis; this.Reposition(); } }
public void SetMasterAreaAxis(LayoutAxis masterAreaAxis) { if (this.masterAreaAxis != masterAreaAxis) { this.masterAreaAxis = masterAreaAxis; this.Reposition(); } }
public LayoutDimensions GetLayoutDimensions(LayoutAxis axis) { var hovLayoutGroup = (HorizontalOrVerticalLayoutGroup)ICellLayout.LayoutGroup; bool isVertical = hovLayoutGroup is VerticalLayoutGroup; float layoutPadding = (axis != 0) ? hovLayoutGroup.padding.vertical : hovLayoutGroup.padding.horizontal; bool initialAxis = isVertical ^ (axis == LayoutAxis.Vertical); var layoutDimensions = new LayoutDimensions() { Min = layoutPadding, Preferred = layoutPadding, Flexible = 0f }; foreach (CellData cellRecord in CellRecords) { bool cellNeedsLayoutData = (!cellRecord.LayoutData.DimensionsHorizontalSet && axis == LayoutAxis.Horizontal) || (!cellRecord.LayoutData.DimensionsVerticalSet && axis == LayoutAxis.Vertical); //if (cellNeedsLayoutData) //{ cellRecord.LayoutData.SetDimensions(PrecalculateCellLayoutData(cellRecord, axis), axis); //} float minSize = RecyclerUtil.GetMinSize(cellRecord.LayoutData, (int)axis); float preferredSize = RecyclerUtil.GetPreferredSize(cellRecord.LayoutData, (int)axis); float flexibleSize = RecyclerUtil.GetFlexibleSize(cellRecord.LayoutData, (int)axis); if ((axis != 0) ? hovLayoutGroup.childForceExpandHeight : hovLayoutGroup.childForceExpandWidth) { flexibleSize = Mathf.Max(flexibleSize, 1f); } if (initialAxis) { layoutDimensions.Min = Mathf.Max(minSize + layoutPadding, layoutDimensions.Min); layoutDimensions.Preferred = Mathf.Max(preferredSize + layoutPadding, layoutDimensions.Preferred); layoutDimensions.Flexible = Mathf.Max(flexibleSize, layoutDimensions.Flexible); } else { layoutDimensions.Min += minSize + hovLayoutGroup.spacing; layoutDimensions.Preferred += preferredSize + hovLayoutGroup.spacing; layoutDimensions.Flexible += flexibleSize; } } if (!initialAxis && CellRecords.Count > 0) { layoutDimensions.Min -= hovLayoutGroup.spacing; layoutDimensions.Preferred -= hovLayoutGroup.spacing; } layoutDimensions.Preferred = Mathf.Max(layoutDimensions.Min, layoutDimensions.Preferred); return(layoutDimensions); }
private static string GetAreaSymbol(int count, LayoutAxis axis) { switch (axis) { case LayoutAxis.LeftToRight: case LayoutAxis.RightToLeft: return("|"); case LayoutAxis.TopToBottom: case LayoutAxis.BottomToTop: return("="); default: // LayoutAxis.Monocle return(count.ToString()); } }
public LayoutDimensions PrecalculateCellLayoutData(CellData cellData, LayoutAxis axis) { // Set up the proxy cell to reflect the same layout and rect as our cell would be instantiated as var recyclerCell = CellPool.CellProxy.GetComponent <IRecyclableCell>(); var proxyRtx = (RectTransform)CellPool.CellProxy.transform; recyclerCell.OnCellInstantiate(); recyclerCell.OnCellShow(cellData); LayoutRebuilder.ForceRebuildLayoutImmediate(proxyRtx); LayoutAxis initialAxis = (ICellLayout.LayoutGroup is VerticalCellLayout) ? LayoutAxis.Horizontal : LayoutAxis.Vertical; LayoutAxis secondaryAxis = (initialAxis == LayoutAxis.Horizontal) ? LayoutAxis.Vertical : LayoutAxis.Horizontal; var iLayout = CellPool.CellProxy.GetComponent <ILayoutElement>(); Action <string> dbgPrint = (string msg) => { //Debug.Log(string.Format("{0} proxyRtx.rect.size = {1} preferredSize = {2} minSize = {3} flexibleSize = {4}", // msg, // proxyRtx.rect.size, // new Vector2(iLayout.preferredWidth, iLayout.preferredHeight), // new Vector2(iLayout.minWidth, iLayout.minHeight), // new Vector2(iLayout.flexibleWidth, iLayout.flexibleHeight))); }; Action <string> placeCell = (string message) => { PlaceCell(proxyRtx, initialAxis); dbgPrint(message + "Initial Axis: "); PlaceCell(proxyRtx, secondaryAxis); dbgPrint(message + "Secondary Axis: "); }; placeCell("FIRST FRAME (1): "); LayoutRebuilder.ForceRebuildLayoutImmediate(proxyRtx); placeCell("FIRST FRAME (2): "); CoroutineRunner.WaitForEndOfFrame(() => placeCell("SECOND FRAME: ")); return(GetProxyLayoutDimensions(axis)); }
public void SetDimensions(LayoutDimensions dims, LayoutAxis axis) { if (axis == LayoutAxis.Horizontal) { //if (!DimensionsHorizontalSet) //{ SetWidth(dims); DimensionsHorizontalSet = true; //} } else { //if (!DimensionsVerticalSet) //{ SetHeight(dims); DimensionsVerticalSet = true; //} } }
public TileLayout(LayoutAxis layoutAxis = LayoutAxis.LeftToRight, LayoutAxis masterAreaAxis = LayoutAxis.Monocle, LayoutAxis stackAreaAxis = LayoutAxis.TopToBottom, double masterAreaFactor = 0.6, int masterAreaWindowsCount = 1) { this.layoutAxis = layoutAxis; this.masterAreaAxis = masterAreaAxis; this.stackAreaAxis = stackAreaAxis; if (masterAreaFactor > 1) { masterAreaFactor = 1; } else if (masterAreaFactor < 0) { masterAreaFactor = 0; } this.masterAreaFactor = masterAreaFactor; if (masterAreaWindowsCount < 0) { masterAreaWindowsCount = 0; } this.masterAreaWindowsCount = masterAreaWindowsCount; }
private void LayoutChild( AndroidView child, LayoutParams layoutParams, Rectangle arrangeRect) { if (null == child) { throw new ArgumentNullException(nameof(child)); } if (null == layoutParams) { throw new ArgumentNullException(nameof(layoutParams)); } // Handle gravity flags LayoutAxis axis = GetLayoutAxis(layoutParams.DockRegion); float childLeft = arrangeRect.Left + layoutParams.LeftMargin; float childLayoutWidth = child.MeasuredWidth; var horizontalGravityFlags = layoutParams.Gravity & GravityFlags.HorizontalGravityMask; if (horizontalGravityFlags == GravityFlags.CenterHorizontal) { childLeft = arrangeRect.Left + layoutParams.LeftMargin + ((arrangeRect.Width - (layoutParams.LeftMargin + child.MeasuredWidth + layoutParams.RightMargin)) / 2); } else if (horizontalGravityFlags == GravityFlags.Right) { childLeft = arrangeRect.Right - (child.MeasuredWidth + layoutParams.RightMargin); } else if ((horizontalGravityFlags == GravityFlags.FillHorizontal) && (axis != LayoutAxis.Horizontal)) { // Check for auto-dimension if ((child.MeasuredWidth == global::Android.Views.ViewGroup.LayoutParams.MatchParent) || (child.MeasuredWidth == global::Android.Views.ViewGroup.LayoutParams.WrapContent)) { // Use the entire available width (minus the margins) to layout the child childLayoutWidth = arrangeRect.Width - layoutParams.LeftMargin - layoutParams.RightMargin; } else { // Center in the available width childLeft = arrangeRect.Left + layoutParams.LeftMargin + ((arrangeRect.Width - (layoutParams.LeftMargin + child.MeasuredWidth + layoutParams.RightMargin)) / 2); } } float childTop = arrangeRect.Top + layoutParams.TopMargin; float childLayoutHeight = child.MeasuredHeight; var verticalGravityFlags = layoutParams.Gravity & GravityFlags.VerticalGravityMask; if (verticalGravityFlags == GravityFlags.CenterVertical) { childTop = arrangeRect.Top + layoutParams.TopMargin + ((arrangeRect.Height - (layoutParams.TopMargin + child.MeasuredHeight + layoutParams.BottomMargin)) / 2); } else if (verticalGravityFlags == GravityFlags.Bottom) { childTop = arrangeRect.Bottom - (child.MeasuredHeight + layoutParams.BottomMargin); } else if ((verticalGravityFlags == GravityFlags.FillVertical) && (axis != LayoutAxis.Vertical)) { // Check for auto-dimension if ((child.MeasuredHeight == global::Android.Views.ViewGroup.LayoutParams.MatchParent) || (child.MeasuredHeight == global::Android.Views.ViewGroup.LayoutParams.WrapContent)) { // Use the entire available height (minus the margins) to layout the child childLayoutHeight = arrangeRect.Height - layoutParams.TopMargin - layoutParams.BottomMargin; } else { // Center in the available height childTop = arrangeRect.Top + layoutParams.TopMargin + ((arrangeRect.Height - (layoutParams.TopMargin + child.MeasuredHeight + layoutParams.BottomMargin)) / 2); } } // Layout the child with gravity taken into account child.Layout( (int)childLeft, (int)childTop, Math.Max(0, (int)(childLeft + childLayoutWidth)), Math.Max(0, (int)(childTop + childLayoutHeight))); }
LayoutDimensions GetProxyLayoutDimensions(LayoutAxis axis) { return(GetCellLayoutDimensions((RectTransform)CellPool.CellProxy.transform, axis)); }
protected override void OnLayout( bool changed, int left, int top, int right, int bottom) { Rectangle layoutRect = new Rectangle( this.PaddingLeft, this.PaddingTop, right - left - this.PaddingLeft - this.PaddingRight, bottom - top - this.PaddingTop - this.PaddingBottom); double childrenTotalWidth = 0; int childCount = this.ChildCount; for (int i = 0; i < childCount; i += 1) { AndroidView child = this.GetChildAt(i); var layoutParams = (BaseLayoutParams)child.LayoutParameters; Size childSize = new Size( child.MeasuredWidth + layoutParams.LeftMargin + layoutParams.RightMargin, child.MeasuredHeight + layoutParams.TopMargin + layoutParams.BottomMargin); childrenTotalWidth += childSize.Width; } // Calculate the spacing between children float childSpacing = 0; int spaceCount = childCount; if (spaceCount > 0) { childSpacing = (float)Math.Max(0, (layoutRect.Width - childrenTotalWidth) / spaceCount); } Rectangle remainingRect = layoutRect; for (int i = 0; i < childCount; i += 1) { AndroidView child = this.GetChildAt(i); var layoutParams = (BaseLayoutParams)child.LayoutParameters; float arrangeWidth = layoutParams.LeftMargin + child.MeasuredWidth + layoutParams.RightMargin + childSpacing; // Handle gravity flags LayoutAxis axis = LayoutAxis.Horizontal; float childLeft = remainingRect.Left + layoutParams.LeftMargin; float childLayoutWidth = child.MeasuredWidth; var horizontalGravityFlags = layoutParams.Gravity & GravityFlags.HorizontalGravityMask; if (horizontalGravityFlags == GravityFlags.CenterHorizontal) { childLeft = remainingRect.Left + (childSpacing / 2) + layoutParams.LeftMargin; } else if (horizontalGravityFlags == GravityFlags.Right) { childLeft = remainingRect.Left + childSpacing + layoutParams.LeftMargin; } else if ((horizontalGravityFlags == GravityFlags.FillHorizontal) && (axis != LayoutAxis.Horizontal)) { // Use the entire available width (minus the margins) to layout the child childLayoutWidth = remainingRect.Width - layoutParams.LeftMargin - layoutParams.RightMargin; arrangeWidth = remainingRect.Width; } float childTop = remainingRect.Top + layoutParams.TopMargin; float childLayoutHeight = child.MeasuredHeight; var verticalGravityFlags = layoutParams.Gravity & GravityFlags.VerticalGravityMask; if (verticalGravityFlags == GravityFlags.CenterVertical) { childTop = remainingRect.Top + layoutParams.TopMargin + ((remainingRect.Height - (layoutParams.TopMargin + child.MeasuredHeight + layoutParams.BottomMargin)) / 2); } else if (verticalGravityFlags == GravityFlags.Bottom) { childTop = remainingRect.Bottom - (child.MeasuredHeight + layoutParams.BottomMargin); } else if ((verticalGravityFlags == GravityFlags.FillVertical) && (axis != LayoutAxis.Vertical)) { // Use the entire available height (minus the margins) to layout the child childLayoutHeight = remainingRect.Height - layoutParams.TopMargin - layoutParams.BottomMargin; } // Layout the child with gravity taken into account child.Layout( (int)childLeft, (int)childTop, Math.Max(0, (int)(childLeft + childLayoutWidth)), Math.Max(0, (int)(childTop + childLayoutHeight))); remainingRect.X += arrangeWidth; remainingRect.Width = Math.Max(0, remainingRect.Width - arrangeWidth); } }
private static string GetAreaSymbol(int count, LayoutAxis axis) { switch (axis) { case LayoutAxis.LeftToRight: case LayoutAxis.RightToLeft: return "|"; case LayoutAxis.TopToBottom: case LayoutAxis.BottomToTop: return "="; default: // LayoutAxis.Monocle return count.ToString(); } }
private void LayoutChild( NativeView child, LayoutParams layoutParams, Rectangle arrangeRect) { if (null == child) { throw new ArgumentNullException(nameof(child)); } if (null == layoutParams) { throw new ArgumentNullException(nameof(layoutParams)); } var childLayoutProperties = child.LayoutProperties(); // Handle gravity flags LayoutAxis axis = GetLayoutAxis(layoutParams.DockRegion); float childLeft = arrangeRect.Left + layoutParams.Margin.Left; float childLayoutWidth = childLayoutProperties.MeasuredSize.Width; var horizontalAlignment = layoutParams.HorizontalAlignment; if (horizontalAlignment == LayoutAlignment.Center) { childLeft = arrangeRect.Left + layoutParams.Margin.Left + ((arrangeRect.Width - (layoutParams.Margin.Left + childLayoutProperties.MeasuredSize.Width + layoutParams.Margin.Right)) / 2); } else if (horizontalAlignment == LayoutAlignment.End) { childLeft = arrangeRect.Right - (childLayoutProperties.MeasuredSize.Width + layoutParams.Margin.Right); } else if ((horizontalAlignment == LayoutAlignment.Fill) && (axis != LayoutAxis.Horizontal)) { // Use the entire available width (minus the margins) to layout the child childLayoutWidth = arrangeRect.Width - layoutParams.Margin.Left - layoutParams.Margin.Right; } float childTop = arrangeRect.Top + layoutParams.Margin.Top; float childLayoutHeight = childLayoutProperties.MeasuredSize.Height; var verticalAlignment = layoutParams.VerticalAlignment; if (verticalAlignment == LayoutAlignment.Center) { childTop = arrangeRect.Top + layoutParams.Margin.Top + ((arrangeRect.Height - (layoutParams.Margin.Top + childLayoutProperties.MeasuredSize.Height + layoutParams.Margin.Bottom)) / 2); } else if (verticalAlignment == LayoutAlignment.End) { childTop = arrangeRect.Bottom - (childLayoutProperties.MeasuredSize.Height + layoutParams.Margin.Bottom); } else if ((verticalAlignment == LayoutAlignment.Fill) && (axis != LayoutAxis.Vertical)) { // Use the entire available height (minus the margins) to layout the child childLayoutHeight = arrangeRect.Height - layoutParams.Margin.Top - layoutParams.Margin.Bottom; } // Layout the child with gravity taken into account child.Layout( childLeft, childTop, Math.Max(0, childLeft + childLayoutWidth), Math.Max(0, childTop + childLayoutHeight)); }
LayoutDimensions GetCellLayoutDimensions(RectTransform cellRtx, LayoutAxis axis) { var layoutDims = new LayoutDimensions(); var rectTransform = (RectTransform)ICellLayout.LayoutGroup.transform; var hovLayoutGroup = (HorizontalOrVerticalLayoutGroup)ICellLayout.LayoutGroup; bool isVertical = hovLayoutGroup is VerticalLayoutGroup; bool layoutChildForceExpand = (axis != 0) ? hovLayoutGroup.childForceExpandHeight : hovLayoutGroup.childForceExpandWidth; float layoutPadding = (axis != 0) ? ICellLayout.LayoutGroup.padding.vertical : ICellLayout.LayoutGroup.padding.horizontal; if (ICellLayout.CellLayout != null && ICellLayout.CellLayout.CellPrefab) { float rectSizeOnCurrAxis = rectTransform.rect.size[(int)axis]; bool initialAxis = isVertical ^ axis == LayoutAxis.Vertical; if (initialAxis) { for (int i = 0; i < ICellLayout.CellLayout.CellRecords.Count; i++) { CellData currRecord = ICellLayout.CellLayout.CellRecords[i]; layoutDims.Min = RecyclerUtil.GetMinSize(cellRtx, (int)axis); layoutDims.Preferred = RecyclerUtil.GetPreferredSize(cellRtx, (int)axis); layoutDims.Flexible = RecyclerUtil.GetFlexibleSize(cellRtx, (int)axis); if (layoutChildForceExpand) { layoutDims.Flexible = Mathf.Max(layoutDims.Flexible, 1f); } float childSize = Mathf.Clamp( layoutPadding, layoutDims.Min, (layoutDims.Flexible <= 0f) ? layoutDims.Preferred : rectSizeOnCurrAxis); float startOffset = RecyclerUtil.GetStartOffset(ICellLayout.LayoutGroup, (int)axis, childSize); currRecord.RectTransformData.SetInsetAndSizeFromParentEdge( (axis != 0) ? RectTransform.Edge.Top : RectTransform.Edge.Left, startOffset, childSize); } } else { float childPos = layoutPadding; float totalLayoutMinSize = RecyclerUtil.GetTotalMinSize(ICellLayout.LayoutGroup, (int)axis); float totalLayoutPreferredSize = RecyclerUtil.GetTotalPreferredSize(ICellLayout.LayoutGroup, (int)axis); float totalLayoutFlexibleSize = RecyclerUtil.GetTotalFlexibleSize(ICellLayout.LayoutGroup, (int)axis); bool layoutRectLargeEnoughForPreferred = totalLayoutPreferredSize < rectSizeOnCurrAxis; if ((totalLayoutFlexibleSize == 0f) && layoutRectLargeEnoughForPreferred) { float requiredSpaceWithoutPadding = totalLayoutPreferredSize - layoutPadding; childPos = RecyclerUtil.GetStartOffset( ICellLayout.LayoutGroup, (int)axis, requiredSpaceWithoutPadding); } float flexibleScalar = 0f; if (!(totalLayoutMinSize == totalLayoutPreferredSize)) { flexibleScalar = Mathf.Clamp01( (rectSizeOnCurrAxis - totalLayoutMinSize) / totalLayoutPreferredSize - totalLayoutMinSize); } float sizeRatio = 0f; if (rectSizeOnCurrAxis > totalLayoutPreferredSize && totalLayoutFlexibleSize > 0f) { sizeRatio = (rectSizeOnCurrAxis - totalLayoutPreferredSize) / totalLayoutFlexibleSize; } for (int j = 0; j < ICellLayout.CellLayout.CellRecords.Count; j++) { CellData currRecord = ICellLayout.CellLayout.CellRecords[j]; RectTransform childRect = (RectTransform)ICellLayout.CellLayout.CellPrefab.transform; layoutDims.Min = RecyclerUtil.GetMinSize(childRect, (int)axis); layoutDims.Preferred = RecyclerUtil.GetPreferredSize(childRect, (int)axis); layoutDims.Flexible = RecyclerUtil.GetFlexibleSize(childRect, (int)axis); if (layoutChildForceExpand) { layoutDims.Flexible = Mathf.Max(layoutDims.Flexible, 1f); } float childSize = Mathf.Lerp(layoutDims.Min, layoutDims.Preferred, flexibleScalar) + (layoutDims.Flexible * sizeRatio); currRecord.RectTransformData.SetInsetAndSizeFromParentEdge( (axis != 0) ? RectTransform.Edge.Top : RectTransform.Edge.Left, childPos, childSize); childPos += childSize + hovLayoutGroup.spacing; } } } return(layoutDims); }
public void SetCellsDimensions(LayoutAxis axis) { var hovLayoutGroup = (HorizontalOrVerticalLayoutGroup)ICellLayout.LayoutGroup; float rectSizeOnCurrAxis = ((RectTransform)hovLayoutGroup.transform).rect.size[(int)axis]; bool initialAxis = (hovLayoutGroup is VerticalLayoutGroup) ^ (axis == LayoutAxis.Vertical); float layoutPadding = (axis != 0) ? hovLayoutGroup.padding.vertical : hovLayoutGroup.padding.horizontal; float layoutSizeWithoutPadding = rectSizeOnCurrAxis - layoutPadding; bool layoutChildForceExpand = (axis != 0) ? hovLayoutGroup.childForceExpandHeight : hovLayoutGroup.childForceExpandWidth; if (initialAxis) { foreach (CellData cellRecord in CellRecords) { float minSize = RecyclerUtil.GetMinSize(cellRecord.LayoutData, (int)axis); float preferredSize = RecyclerUtil.GetPreferredSize(cellRecord.LayoutData, (int)axis); float flexibleSize = RecyclerUtil.GetFlexibleSize(cellRecord.LayoutData, (int)axis); if (layoutChildForceExpand) { flexibleSize = Mathf.Max(flexibleSize, 1f); } float childSize = Mathf.Clamp( layoutSizeWithoutPadding, minSize, (flexibleSize <= 0f) ? preferredSize : rectSizeOnCurrAxis); float startOffset = RecyclerUtil.GetStartOffset(hovLayoutGroup, (int)axis, childSize); cellRecord.RectTransformData.SetInsetAndSizeFromParentEdge( (axis != 0) ? RectTransform.Edge.Top : RectTransform.Edge.Left, startOffset, childSize); } } else { float childPos = ((axis != 0) ? hovLayoutGroup.padding.top : hovLayoutGroup.padding.left); float layoutTotalMinSize = RecyclerUtil.GetTotalMinSize(hovLayoutGroup, (int)axis); float layoutTotalPreferredSize = RecyclerUtil.GetTotalPreferredSize(hovLayoutGroup, (int)axis); float layoutTotalFlexibleSize = RecyclerUtil.GetTotalFlexibleSize(hovLayoutGroup, (int)axis); if (layoutTotalFlexibleSize == 0f && layoutTotalPreferredSize < rectSizeOnCurrAxis) { childPos = RecyclerUtil.GetStartOffset(hovLayoutGroup, (int)axis, layoutPadding); } float minToPreferredRatio = 0f; if (layoutTotalMinSize != layoutTotalPreferredSize) { float rectSizeAfterMinSize = rectSizeOnCurrAxis - layoutTotalMinSize; float deltaBetweenMinAndPreferred = layoutTotalPreferredSize - layoutTotalMinSize; minToPreferredRatio = Mathf.Clamp01(rectSizeAfterMinSize / deltaBetweenMinAndPreferred); } float remainingSizeRatio = 0f; if (rectSizeOnCurrAxis > layoutTotalPreferredSize && layoutTotalFlexibleSize > 0f) { remainingSizeRatio = (rectSizeOnCurrAxis - layoutTotalPreferredSize) / layoutTotalFlexibleSize; } foreach (CellData cellRecord in CellRecords) { float minSize = RecyclerUtil.GetMinSize(cellRecord.LayoutData, (int)axis); float preferredSize = RecyclerUtil.GetPreferredSize(cellRecord.LayoutData, (int)axis); float flexibleSize = RecyclerUtil.GetFlexibleSize(cellRecord.LayoutData, (int)axis); if (layoutChildForceExpand) { flexibleSize = Mathf.Max(flexibleSize, 1f); } float childSize = Mathf.Lerp(minSize, preferredSize, minToPreferredRatio); childSize += flexibleSize * remainingSizeRatio; cellRecord.RectTransformData.SetInsetAndSizeFromParentEdge( (axis != 0) ? RectTransform.Edge.Top : RectTransform.Edge.Left, childPos, childSize); childPos += childSize + hovLayoutGroup.spacing; } } }
protected override void OnMeasure( int widthMeasureSpec, int heightMeasureSpec) { Size availableSize = new Size( MeasureSpec.GetSize(widthMeasureSpec) - this.PaddingLeft - this.PaddingRight, MeasureSpec.GetSize(heightMeasureSpec) - this.PaddingTop - this.PaddingBottom); #if DEBUG var widthMode = MeasureSpec.GetMode(widthMeasureSpec); var heightMode = MeasureSpec.GetMode(heightMeasureSpec); #endif // Keep track of the space used by children double usedLeft = 0; double usedTop = 0; double usedRight = 0; double usedBottom = 0; // Keep track of the starting point for measuring each dimension // in support of overlay regions double startLeft = 0; double startTop = 0; double startRight = 0; double startBottom = 0; double minWidth = 0; double minHeight = 0; var remainingSize = availableSize; int childState = 0; int childCount = this.ChildCount; for (int i = 0; i < childCount; i += 1) { AndroidView child = this.GetChildAt(i); if (child.Visibility == ViewStates.Gone) { continue; } var layoutParams = (LayoutParams)child.LayoutParameters; var dockRegion = layoutParams.DockRegion; // Arrange all but the center elements if (dockRegion != DockRegion.CenterOverlay) { LayoutAxis axis = GetLayoutAxis(dockRegion); // Save the child width and height int childWidth = layoutParams.Width; int childHeight = layoutParams.Height; // Overwrite child MatchParent size on the stacking axis if ((axis == LayoutAxis.Horizontal) && (childWidth == LayoutParams.MatchParent)) { childWidth = LayoutParams.WrapContent; } else if ((axis == LayoutAxis.Vertical) && (childHeight == LayoutParams.MatchParent)) { childHeight = LayoutParams.WrapContent; } // Measure the child this.MeasureChildWithMarginsOverride( child, widthMeasureSpec, (int)(usedLeft + usedRight), childWidth, heightMeasureSpec, (int)(usedTop + usedBottom), childHeight); Size childUsedSize = GetChildUsedSizeWithMargins(child, layoutParams); if (dockRegion == DockRegion.Left) { usedLeft = Math.Max(usedLeft, startLeft + childUsedSize.Width); startLeft += childUsedSize.Width; minHeight = Math.Max(minHeight, usedTop + usedBottom + childUsedSize.Height); } else if (dockRegion == DockRegion.LeftOverlay) { usedLeft = Math.Max(usedLeft, startLeft + childUsedSize.Width); minHeight = Math.Max(minHeight, usedTop + usedBottom + childUsedSize.Height); } else if (dockRegion == DockRegion.Top) { usedTop = Math.Max(usedTop, startTop + childUsedSize.Height); startTop += childUsedSize.Height; minWidth = Math.Max(minWidth, usedLeft + usedRight + childUsedSize.Width); } else if (dockRegion == DockRegion.TopOverlay) { usedTop = Math.Max(usedTop, startTop + childUsedSize.Height); minWidth = Math.Max(minWidth, usedLeft + usedRight + childUsedSize.Width); } else if (dockRegion == DockRegion.Right) { usedRight = Math.Max(usedRight, startRight + childUsedSize.Width); startRight += childUsedSize.Width; minHeight = Math.Max(minHeight, usedTop + usedBottom + childUsedSize.Height); } else if (dockRegion == DockRegion.RightOverlay) { usedRight = Math.Max(usedRight, startRight + childUsedSize.Width); minHeight = Math.Max(minHeight, usedTop + usedBottom + childUsedSize.Height); } else if (dockRegion == DockRegion.Bottom) { usedBottom = Math.Max(usedBottom, startBottom + childUsedSize.Height); startBottom += childUsedSize.Height; minWidth = Math.Max(minWidth, usedLeft + usedRight + childUsedSize.Width); } else if (dockRegion == DockRegion.BottomOverlay) { usedBottom = Math.Max(usedBottom, startBottom + childUsedSize.Height); minWidth = Math.Max(minWidth, usedLeft + usedRight + childUsedSize.Width); } else { throw new NotSupportedException("Unsupported dock region."); } // Update the remaining size remainingSize = new Size( (float)Math.Max(0, availableSize.Width - startLeft - startRight), (float)Math.Max(0, availableSize.Height - startTop - startBottom)); // Update the child state childState = CombineMeasuredStates(childState, child.MeasuredState); } } // Measure the center elements now that the center size has been completely calculated Size usedCenterSize = new Size(0, 0); for (int i = 0; i < childCount; i += 1) { AndroidView child = this.GetChildAt(i); if (child.Visibility == ViewStates.Gone) { continue; } var layoutParams = (LayoutParams)child.LayoutParameters; var dockRegion = layoutParams.DockRegion; // Arrange only the center elements if (dockRegion != DockRegion.CenterOverlay) { continue; } this.MeasureChildWithMarginsOverride( child, widthMeasureSpec, (int)(usedLeft + usedRight), layoutParams.Width, heightMeasureSpec, (int)(usedTop + usedBottom), layoutParams.Height); Size childUsedSize = GetChildUsedSizeWithMargins(child, layoutParams); usedCenterSize = new Size( Math.Max(usedCenterSize.Width, childUsedSize.Width), Math.Max(usedCenterSize.Height, childUsedSize.Height)); childState = CombineMeasuredStates(childState, child.MeasuredState); } // Calculate the total size used by children Size usedSize = new Size( (float)Math.Max(minWidth, usedLeft + usedRight + usedCenterSize.Width), (float)Math.Max(minHeight, usedTop + usedBottom + usedCenterSize.Height)); // Default the final size to the size used by children var finalSize = new Size( usedSize.Width + this.PaddingLeft + this.PaddingRight, usedSize.Height + this.PaddingTop + this.PaddingBottom); int dimensionX = ResolveSizeAndState((int)finalSize.Width, widthMeasureSpec, childState); int dimensionY = ResolveSizeAndState((int)finalSize.Height, heightMeasureSpec, childState); this.SetMeasuredDimension(dimensionX, dimensionY); }
protected override void OnLayout( bool changed, float left, float top, float right, float bottom) { var layoutProperties = this.LayoutProperties(); Rectangle layoutRect = new Rectangle( layoutProperties.Padding.Left, layoutProperties.Padding.Top, right - left - layoutProperties.Padding.Left - layoutProperties.Padding.Right, bottom - top - layoutProperties.Padding.Top - layoutProperties.Padding.Bottom); float childrenTotalWidth = 0; int childCount = this.ChildCount; for (int i = 0; i < childCount; i += 1) { var child = this.GetChildAt(i); var layoutParams = (LayoutParams)child.LayoutParameters(); var childSize = GetChildUsedSizeWithMargins(child, layoutParams); childrenTotalWidth += childSize.Width; } // Calculate the spacing between children float childSpacing = 0; int spaceCount = childCount; if (spaceCount > 0) { childSpacing = (float)Math.Max(0, (layoutRect.Width - childrenTotalWidth) / spaceCount); } Rectangle remainingRect = layoutRect; for (int i = 0; i < childCount; i += 1) { var child = this.GetChildAt(i); var layoutParams = (LayoutParams)child.LayoutParameters(); var childLayoutProperties = child.LayoutProperties(); float arrangeWidth = layoutParams.Margin.Left + childLayoutProperties.MeasuredSize.Width + layoutParams.Margin.Right + childSpacing; // Handle gravity flags LayoutAxis axis = LayoutAxis.Horizontal; float childLeft = remainingRect.Left + layoutParams.Margin.Left; float childLayoutWidth = childLayoutProperties.MeasuredSize.Width; var horizontalAlignment = layoutParams.HorizontalAlignment; if (horizontalAlignment == LayoutAlignment.Center) { childLeft = remainingRect.Left + (childSpacing / 2) + layoutParams.Margin.Left; } else if (horizontalAlignment == LayoutAlignment.End) { childLeft = remainingRect.Left + childSpacing + layoutParams.Margin.Left; } else if ((horizontalAlignment == LayoutAlignment.Fill) && (axis != LayoutAxis.Horizontal)) { // Use the entire available width (minus the margins) to layout the child childLayoutWidth = remainingRect.Width - layoutParams.Margin.Left - layoutParams.Margin.Right; arrangeWidth = remainingRect.Width; } float childTop = remainingRect.Top + layoutParams.Margin.Top; float childLayoutHeight = childLayoutProperties.MeasuredSize.Height; var verticalAlignment = layoutParams.VerticalAlignment; if (verticalAlignment == LayoutAlignment.Center) { childTop = remainingRect.Top + layoutParams.Margin.Top + ((remainingRect.Height - (layoutParams.Margin.Top + childLayoutProperties.MeasuredSize.Height + layoutParams.Margin.Bottom)) / 2); } else if (verticalAlignment == LayoutAlignment.End) { childTop = remainingRect.Bottom - (childLayoutProperties.MeasuredSize.Height + layoutParams.Margin.Bottom); } else if ((verticalAlignment == LayoutAlignment.Fill) && (axis != LayoutAxis.Vertical)) { // Use the entire available height (minus the margins) to layout the child childLayoutHeight = remainingRect.Height - layoutParams.Margin.Top - layoutParams.Margin.Bottom; } // Layout the child with gravity taken into account child.Layout( childLeft, childTop, Math.Max(0, childLeft + childLayoutWidth), Math.Max(0, childTop + childLayoutHeight)); remainingRect.X += arrangeWidth; remainingRect.Width = Math.Max(0, remainingRect.Width - arrangeWidth); } }
protected override void OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec) { var layoutProperties = this.LayoutProperties(); Size availableSize = new Size( widthMeasureSpec.Size - layoutProperties.Padding.Left - layoutProperties.Padding.Right, heightMeasureSpec.Size - layoutProperties.Padding.Top - layoutProperties.Padding.Bottom); // Keep track of the space used by children float usedLeft = 0; float usedTop = 0; float usedRight = 0; float usedBottom = 0; // Keep track of the starting point for measuring each dimension // in support of overlay regions float startLeft = 0; float startTop = 0; float startRight = 0; float startBottom = 0; float minWidth = 0; float minHeight = 0; var remainingSize = availableSize; var childWidthState = MeasuredStateFlags.None; var childHeightState = MeasuredStateFlags.None; int childCount = this.ChildCount; for (int i = 0; i < childCount; i += 1) { NativeView child = this.GetChildAt(i); if (child.Hidden) { continue; } var layoutParams = (LayoutParams)child.LayoutParameters(); var dockRegion = layoutParams.DockRegion; // Arrange all but the center elements if (dockRegion != DockRegion.CenterOverlay) { LayoutAxis axis = GetLayoutAxis(dockRegion); // Save the child width and height var childWidth = layoutParams.Width; var childHeight = layoutParams.Height; // Overwrite child MatchParent size on the stacking axis if ((axis == LayoutAxis.Horizontal) && (childWidth == LayoutParams.MatchParent)) { childWidth = LayoutParams.WrapContent; } else if ((axis == LayoutAxis.Vertical) && (childHeight == LayoutParams.MatchParent)) { childHeight = LayoutParams.WrapContent; } // Measure the child this.MeasureChildWithMarginsOverride( child, widthMeasureSpec, usedLeft + usedRight, childWidth, heightMeasureSpec, usedTop + usedBottom, childHeight); Size childUsedSize = GetChildUsedSizeWithMargins(child, layoutParams); if (dockRegion == DockRegion.Left) { usedLeft = Math.Max(usedLeft, startLeft + childUsedSize.Width); startLeft += childUsedSize.Width; minHeight = Math.Max(minHeight, usedTop + usedBottom + childUsedSize.Height); } else if (dockRegion == DockRegion.LeftOverlay) { usedLeft = Math.Max(usedLeft, startLeft + childUsedSize.Width); minHeight = Math.Max(minHeight, usedTop + usedBottom + childUsedSize.Height); } else if (dockRegion == DockRegion.Top) { usedTop = Math.Max(usedTop, startTop + childUsedSize.Height); startTop += childUsedSize.Height; minWidth = Math.Max(minWidth, usedLeft + usedRight + childUsedSize.Width); } else if (dockRegion == DockRegion.TopOverlay) { usedTop = Math.Max(usedTop, startTop + childUsedSize.Height); minWidth = Math.Max(minWidth, usedLeft + usedRight + childUsedSize.Width); } else if (dockRegion == DockRegion.Right) { usedRight = Math.Max(usedRight, startRight + childUsedSize.Width); startRight += childUsedSize.Width; minHeight = Math.Max(minHeight, usedTop + usedBottom + childUsedSize.Height); } else if (dockRegion == DockRegion.RightOverlay) { usedRight = Math.Max(usedRight, startRight + childUsedSize.Width); minHeight = Math.Max(minHeight, usedTop + usedBottom + childUsedSize.Height); } else if (dockRegion == DockRegion.Bottom) { usedBottom = Math.Max(usedBottom, startBottom + childUsedSize.Height); startBottom += childUsedSize.Height; minWidth = Math.Max(minWidth, usedLeft + usedRight + childUsedSize.Width); } else if (dockRegion == DockRegion.BottomOverlay) { usedBottom = Math.Max(usedBottom, startBottom + childUsedSize.Height); minWidth = Math.Max(minWidth, usedLeft + usedRight + childUsedSize.Width); } else { throw new NotSupportedException("Unsupported dock region."); } // Update the remaining size remainingSize = new Size( (float)Math.Max(0, availableSize.Width - startLeft - startRight), (float)Math.Max(0, availableSize.Height - startTop - startBottom)); // Update the child state childWidthState |= child.LayoutProperties().MeasuredSize.WidthState; childHeightState |= child.LayoutProperties().MeasuredSize.HeightState; } } // Measure the center elements now that the center size has been completely calculated Size usedCenterSize = new Size(0, 0); for (int i = 0; i < childCount; i += 1) { NativeView child = this.GetChildAt(i); if (child.Hidden) { continue; } var layoutParams = (LayoutParams)child.LayoutParameters(); var dockRegion = layoutParams.DockRegion; // Arrange only the center elements if (dockRegion != DockRegion.CenterOverlay) { continue; } this.MeasureChildWithMarginsOverride( child, widthMeasureSpec, usedLeft + usedRight, layoutParams.Width, heightMeasureSpec, usedTop + usedBottom, layoutParams.Height); Size childUsedSize = GetChildUsedSizeWithMargins(child, layoutParams); usedCenterSize = new Size( Math.Max(usedCenterSize.Width, childUsedSize.Width), Math.Max(usedCenterSize.Height, childUsedSize.Height)); childWidthState |= child.LayoutProperties().MeasuredSize.WidthState; childHeightState |= child.LayoutProperties().MeasuredSize.HeightState; } // Calculate the total size used by children Size usedSize = new Size( (float)Math.Max(minWidth, usedLeft + usedRight + usedCenterSize.Width), (float)Math.Max(minHeight, usedTop + usedBottom + usedCenterSize.Height)); // Default the final size to the size used by children var finalSize = new Size( usedSize.Width + layoutProperties.Padding.Left + layoutProperties.Padding.Right, usedSize.Height + layoutProperties.Padding.Top + layoutProperties.Padding.Bottom); var dimensionX = ResolveSizeAndState(finalSize.Width, widthMeasureSpec, childWidthState); var dimensionY = ResolveSizeAndState(finalSize.Height, heightMeasureSpec, childHeightState); this.SetMeasuredSize(dimensionX, dimensionY); }