开发过程中在集成ui的时候,有时会出现”Parent has a type of layout group component.A child of a layout group should not have a Content Size Fitter component, since it should be driven by the layout group“的warning,其实就是告诉我们,父节点已经有一个layoutGroup了,即使使用contentSizeFitter,也是应该父节点使用来控制子节点的size,那么这里是如何实现控制的呢,如何集成才能不出现这个warning呢,这里通过源码的阅读来解答这个问题。
publicstaticfloatGetPreferredSize(RectTransform rect, int axis) { if (axis == 0) return GetPreferredWidth(rect); return GetPreferredHeight(rect); }
publicstaticfloatGetPreferredWidth(RectTransform rect) { return Mathf.Max(GetLayoutProperty(rect, e => e.minWidth, 0), GetLayoutProperty(rect, e => e.preferredWidth, 0)); } publicstaticfloatGetLayoutProperty(RectTransform rect, System.Func<ILayoutElement, float> property, float defaultValue, out ILayoutElement source) { source = null; if (rect == null) return0; float min = defaultValue; int maxPriority = System.Int32.MinValue; var components = ListPool<Component>.Get(); rect.GetComponents(typeof(ILayoutElement), components);
for (int i = 0; i < components.Count; i++) { var layoutComp = components[i] as ILayoutElement; if (layoutComp is Behaviour && !((Behaviour)layoutComp).isActiveAndEnabled) continue;
int priority = layoutComp.layoutPriority; // If this layout components has lower priority than a previously used, ignore it. if (priority < maxPriority) continue; float prop = property(layoutComp); // If this layout property is set to a negative value, it means it should be ignored. if (prop < 0) continue;
// If this layout component has higher priority than all previous ones, // overwrite with this one's value. if (priority > maxPriority) { min = prop; maxPriority = priority; source = layoutComp; } // If the layout component has the same priority as a previously used, // use the largest of the values with the same priority. elseif (prop > min) { min = prop; source = layoutComp; } }
for (int i = 0; i < rectChildren.Count; i++) { RectTransform child = rectChildren[i]; float min, preferred, flexible; GetChildSizes(child, axis, controlSize, childForceExpandSize, out min, out preferred, out flexible);
privatevoidSetCellsAlongAxis(int axis) { // Normally a Layout Controller should only set horizontal values when invoked for the horizontal axis // and only vertical values when invoked for the vertical axis. // However, in this case we set both the horizontal and vertical position when invoked for the vertical axis. // Since we only set the horizontal position and not the size, it shouldn't affect children's layout, // and thus shouldn't break the rule that all horizontal layout must be calculated before all vertical layout.
if (axis == 0) { // Only set the sizes when invoked for horizontal axis, not the positions. for (int i = 0; i < rectChildren.Count; i++) { RectTransform rect = rectChildren[i];
for (int i = 0; i < rectChildren.Count; i++) { int positionX; int positionY; if (startAxis == Axis.Horizontal) { positionX = i % cellsPerMainAxis; positionY = i / cellsPerMainAxis; } else { positionX = i / cellsPerMainAxis; positionY = i % cellsPerMainAxis; }
// Set size to min or preferred size if (fitting == FitMode.MinSize) rectTransform.SetSizeWithCurrentAnchors((RectTransform.Axis)axis, LayoutUtility.GetMinSize(m_Rect, axis)); else rectTransform.SetSizeWithCurrentAnchors((RectTransform.Axis)axis, LayoutUtility.GetPreferredSize(m_Rect, axis)); }
publicvoidRebuild(CanvasUpdate executing) { switch (executing) { case CanvasUpdate.Layout: // It's unfortunate that we'll perform the same GetComponents querys for the tree 2 times, // but each tree have to be fully iterated before going to the next action, // so reusing the results would entail storing results in a Dictionary or similar, // which is probably a bigger overhead than performing GetComponents multiple times. PerformLayoutCalculation(m_ToRebuild, e => (e as ILayoutElement).CalculateLayoutInputHorizontal()); PerformLayoutControl(m_ToRebuild, e => (e as ILayoutController).SetLayoutHorizontal()); PerformLayoutCalculation(m_ToRebuild, e => (e as ILayoutElement).CalculateLayoutInputVertical()); PerformLayoutControl(m_ToRebuild, e => (e as ILayoutController).SetLayoutVertical()); break; } }
privatevoidPerformLayoutCalculation(RectTransform rect, UnityAction<Component> action) { if (rect == null) return;
var components = ListPool<Component>.Get(); rect.GetComponents(typeof(ILayoutElement), components); StripDisabledBehavioursFromList(components);
// If there are no controllers on this rect we can skip this entire sub-tree // We don't need to consider controllers on children deeper in the sub-tree either, // since they will be their own roots. if (components.Count > 0 || rect.GetComponent(typeof(ILayoutGroup))) { // Layout calculations needs to executed bottom up with children being done before their parents, // because the parent calculated sizes rely on the sizes of the children.
for (int i = 0; i < rect.childCount; i++) PerformLayoutCalculation(rect.GetChild(i) as RectTransform, action);
for (int i = 0; i < components.Count; i++) action(components[i]); }
ListPool<Component>.Release(components); }
privatevoidPerformLayoutControl(RectTransform rect, UnityAction<Component> action) { if (rect == null) return;
var components = ListPool<Component>.Get(); rect.GetComponents(typeof(ILayoutController), components); StripDisabledBehavioursFromList(components);
// If there are no controllers on this rect we can skip this entire sub-tree // We don't need to consider controllers on children deeper in the sub-tree either, // since they will be their own roots. if (components.Count > 0) { // Layout control needs to executed top down with parents being done before their children, // because the children rely on the sizes of the parents.
// First call layout controllers that may change their own RectTransform for (int i = 0; i < components.Count; i++) if (components[i] is ILayoutSelfController) action(components[i]);
// Then call the remaining, such as layout groups that change their children, taking their own RectTransform size into account. for (int i = 0; i < components.Count; i++) if (!(components[i] is ILayoutSelfController)) action(components[i]);
for (int i = 0; i < rect.childCount; i++) PerformLayoutControl(rect.GetChild(i) as RectTransform, action); }