最近在做项目的时候,遇到了一个需求,TEXT组件的大小需要根据文本内容自适应大小区域。这个解决起来很简单,用一个ContentSizeFitter
组件就可以了,不过考虑到这些TEXT文本一般都是配备大小相似的IMAGE作为背景的,以往我的做法就是在IMAGE层再套一个ContentSizeFitter
&Horizonal Layout Group
来匹配TEXT上的ContentSizeFitter
,然后再利用LayoutElement
组件给IMAGE这个背景图限制最小的宽度和高度。这次就想能不能自己自定义下类似的功能。
于是先去看了下UGUI的源码里ContentSizeFitter
的实现方式,我这个看的是2018.4版本的源码,
Unity开源库地址
UGUI源码GIT地址:https://bitbucket.org/Unity-Technologies/ui
ContentSizeFitter
代码很简单,一百多行,很容易就找到了疑似自适应的关键函数
private void HandleSelfFittingAlongAxis(int axis)
{
FitMode fitting = (axis == 0 ? horizontalFit : verticalFit);
if (fitting == FitMode.Unconstrained)
{
// Keep a reference to the tracked transform, but don't control its properties:
m_Tracker.Add(this, rectTransform, DrivenTransformProperties.None);
return;
}
m_Tracker.Add(this, rectTransform, (axis == 0 ? DrivenTransformProperties.SizeDeltaX : DrivenTransformProperties.SizeDeltaY));
// 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));
}
/// <summary>
/// Calculate and apply the horizontal component of the size to the RectTransform
/// </summary>
public virtual void SetLayoutHorizontal()
{
m_Tracker.Clear();
HandleSelfFittingAlongAxis(0);
}
/// <summary>
/// Calculate and apply the vertical component of the size to the RectTransform
/// </summary>
public virtual void SetLayoutVertical()
{
HandleSelfFittingAlongAxis(1);
}
然后想着继续追踪下SetLayoutHorizontal
和SetLayoutVertical
的函数来源,最后在LayoutRebuilder
里面找到了相关的调用
public void Rebuild(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;
}
}
Rebuild
的函数我没有很理解,不过感觉应该是UGUI重构整个UIMesh的时候需要调用的函数,里面也可以很清楚的看到,是先调用了SetLayoutHorizontal
,后调用SetLayoutVertical
,所以如果我们要做跟随适配的话,重写SetLayoutVertical
的函数应该就可以了。
再深入源码的话,我自己目前的水平就理解不了了,所以先就此停下了。
看下自定义的代码,非常简单。
MConSizeFiiter.cs
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(MConSizeFitterGroup))]
public class MConSizeFitter : ContentSizeFitter
{
[System.NonSerialized] MConSizeFitterGroup _group;
MConSizeFitterGroup Group
{
get
{
if (_group == null)
{
_group = GetComponent<MConSizeFitterGroup>();
}
return _group;
}
}
[System.NonSerialized] protected RectTransform _rect;
protected RectTransform Rect
{
get
{
if (_rect == null)
{
_rect = GetComponent<RectTransform>();
}
return _rect;
}
}
public override void SetLayoutVertical()
{
base.SetLayoutVertical();
//Debug.LogError("y1111");
if (Group != null && Rect!= null)
{
var list = Group.items;
Vector2 result;
for (int i = 0; i < list.Count; i++)
{
result = Rect.sizeDelta + list[i].Pandding;
if (list[i].IsMinOpen)
{
result.x = Mathf.Max(result.x, list[i].LimitValue.x);
result.y = Mathf.Max(result.y, list[i].LimitValue.y);
}
list[i].Rect.sizeDelta = result;
}
}
}
}
可以看到上面需求的一个MConSizeFitterGroup
的类,其原因是我没办法在正常的工程里面创建新的类继承ContentSizeFitterEditor
类,甚至没法引用到UnityEditor.UI
的命名空间,只能借用一个辅助的脚本来引用需要跟随适配的Recttransform
。
using UnityEngine;
using System.Collections.Generic;
public class MConSizeFitterGroup : MonoBehaviour
{
[Header("受到影响的物体")]
public List<MConSizeFitterItem> items = new List<MConSizeFitterItem>();
}
最后一个是跟随适配的物体的脚本,没有直接用Recttransform
是因为想扩展一些功能,比如说我这里加了一些额外空间,最小值边界。
using UnityEngine;
public class MConSizeFitterItem : MonoBehaviour
{
[System.NonSerialized] private RectTransform _rect;
public RectTransform Rect
{
get
{
if (_rect == null)
{
_rect = GetComponent<RectTransform>();
}
return _rect;
}
}
[Header("额外空间")] [SerializeField] Vector2 _padding = Vector2.zero;
public Vector2 Pandding { get { return _padding; } }
[Header("是否开启最小值限制")] [SerializeField] bool _isMinOpen;
public bool IsMinOpen { get { return _isMinOpen; } }
[Header("最小值限制")] [SerializeField] Vector2 _limitValue = Vector2.zero;
public Vector2 LimitValue { get { return _limitValue; } }
}
123.gif
完成。可以看到有最小值的效果,以及文字和图片始终保留了部分空隙。
2020年4月14日 19:39:25
网友评论