假设你做了一个ScrollView,Content下要做一个消息气泡,消息气泡本身就是一个GameObject,里头有一个Text组件,Anchor为(0.5, 1.0),组件上挂好了Content Size Fitter做纵向自适应。
然而消息气泡不能挂Content Size Fitter,因为ScrollView的Content本身就有Content Size Fitter做自适应和LayoutGroup做排版,故用代码做消息气泡的自适应。
public class MessageBubble : MonoBehaviour
{
public TMP_Text msgText;
private RectTransform msgRectTransform;
private RectTransform rectTransform;
private Vector2 msgTextOrgSize;
private Vector2 rectTransformOrgSize;
private void Start()
{
rectTransform = GetComponent<RectTransform>();
msgRectTransform = msgText.GetComponent<RectTransform>();
msgTextOrgSize = msgRectTransform.sizeDelta;
rectTransformOrgSize = rectTransform.sizeDelta;
}
/// <summary>
/// 展示消息内容
/// </summary>
/// <param name="text"></param>
public virtual void AppendMessage(string text)
{
msgText.text = text;
StartCoroutine(UpdateSize());
}
/// <summary>
/// 更新RectTransform的宽高
/// 使用前需确保Text组件加上了ContentSizeFitter,锚点在为(0.5, 1),以本脚本上的RectTransform的Top为适配点
/// </summary>
private IEnumerator UpdateSize()
{
yield return new WaitForEndOfFrame();
LayoutRebuilder.ForceRebuildLayoutImmediate(msgRectTransform);
float yOffset = msgRectTransform.sizeDelta.y - msgTextOrgSize.y;
msgTextOrgSize = msgRectTransform.sizeDelta;
rectTransform.sizeDelta = new Vector2(rectTransformOrgSize.x, rectTransform.sizeDelta.y + yOffset);
LayoutRebuilder.ForceRebuildLayoutImmediate(transform.parent as RectTransform);
}
}
yield return new WaitForEndOfFrame();是必要的,因为更改了Text的内容不代表它身上的Content Size Fitter会马上进行自适应,所以需要等一帧。
等完了,为了避免yOffset的计算误差,在计算前Rebuild一次保证数据精确(原先是再等一帧,但是有些时候还是有误差,不知得等多少帧才可以,故Rebuild)。
计算完毕将数值赋给消息气泡的RectTransform,然后Rebuild ScrollView的Content以应用更改。
注意!ForceRebuild非常吃性能,可能会引起卡顿,不宜频繁调用。
本脚本理论上有优化空间,由于本人水平有限,欢迎大家在评论区提出!