Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题

Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题

目录

Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题

一、简单介绍

二、问题现象

三、问题分析

四、问题解决思路

五、案例解决实现步骤

六、案例关键代码


一、简单介绍

Unity 在开发中,记录一些报错问题,以便后期遇到同样问题处理。

有时我们在开发中,一起使用 ScrollView ,LayoutGroup,ContentSizeFitter,对于简单的排布,一般会很好的自动更新适配,但是有时候复杂了时候未必很好的自动的调整,进行很好适配。

ScrollView 在可滚动区域中显示内容。当你向ScrollView添加内容时,内容会被添加到ScrollView的内容容器(#unity-content-container)中。

Layout Group 翻译为“布局组”,从字面意思就可以理解,可以对一组元素进行动态布局,这里说的动态是,组内元素数量发生变化时,Layout Group可以智能的帮助你重新排版。

ContentSizeFitter 作为一个布局控制器,控制它自己的布局元素的大小。大小由游戏对象上的布局元素组件提供的最小或首选尺寸决定。这样的布局元素可以是图像或文本组件、布局组或布局元素组件。

值得注意的是,当一个矩形变换被缩放时——无论是通过 ContentSizeFitter 还是其他东西——缩放的范围是围绕基准值进行的。这意味着可以使用基准值来控制缩放的方向。

例如,当枢轴位于中心时,ContentSizeFitter 将在所有方向上均匀地扩展矩形变换。当枢轴位于左上角时,内容大小调整器会将矩形变换向下和向右展开。

二、问题现象

现象中,刚开始与生成布局显示是没有问题,当动态添加一些 Item 的时候,会出现 布局显示都不对,会有重叠,或者 Scroll 滑动布局显示不全的现象。

Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题_第1张图片

三、问题分析

案例中:

这里时使用 ScrollView  进行内容过多的时候,可以进行自动滑动浏览内容,其中使用到 LayoutGroup 进行排布,ContentSizeFitter 进行画布自动适配调整大小。

值得注意的是:我这里的嵌套的使用了 LayoutGroup ,和 ContentSizeFitter ,就是Item 中的子物件中还有自动排布和自动适配大小的 LayoutGroup ,和 ContentSizeFitter 组件使用。

由于嵌套,在动态生成中,子物体中也嵌套 LayoutGroup ,和 ContentSizeFitter ,父物体中的 LayoutGroup ,和 ContentSizeFitter 进行计算的时候,就会出现计算错误的现象,布局不对,且Scroll 滑动布局显示不全的现象。

四、问题解决思路

1、Canvas 强制刷新

Canvas.ForceUpdateCanvases();

2、对于unity的gridLayout verticalLayout 或者 horizontalLayout 经常有加入新成员或者改变成员大小后,部件大小、位置不对的问题,一般来说 ,这个方法就能解决

LayoutRebuilder.ForceRebuildLayoutImmediate(rectTransform);

3、用开关 gameobject 等一帧之类的方法解决,然后处理

horizLayoutGroup.CalculateLayoutInputHorizontal();
horizLayoutGroup.CalculateLayoutInputVertical();
horizLayoutGroup.SetLayoutHorizontal();
horizLayoutGroup.SetLayoutVertical();

4、还有就是进行 ContentSizeFitter 组件可以根据其子对象的大小来自动调整父对象的大小。但是,有时在动态添加内容后,ContentSizeFitter 可能无法立即更新,可以进下面

// 手动强制更新 ContentSizeFitter
ContentSizeFitter contentSizeFitter = yourContent.GetComponent();
if (contentSizeFitter != null)
{
    contentSizeFitter.SetLayoutHorizontal();
    contentSizeFitter.SetLayoutVertical();
}

5、延迟更新: 如果进行一次更新无法达到刷新的效果,在向ScrollRect添加内容后,有时需要在下一帧中更新滚动视图和滚动条的范围。这可以通过将更新放置在 LateUpdate 中实现。

  • 如果这里比较耗性能,
  • 可以试着在有数据UI变化的时候触发更新若干时间关闭更新
  • 根据需要合理调整即可
void LateUpdate()
{
    // 在 LateUpdate 中更新内容和滚动条范围
    // 更新 Content 大小
    // 更新滚动条范围
}

五、案例解决实现步骤

1、创建 Unity 工程,进行 UI 布局如下,主要是 Scroll View ,然后再 Content 添加 LayoutGroup ,和 ContentSizeFitter 组件

Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题_第2张图片

Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题_第3张图片

2、要进行实例化的预制体说明,ImageGroup,和 ImageItem

Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题_第4张图片

Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题_第5张图片

3、新建脚本,进行ScrollView 的Content 动态生成,开始预生成部分,然后按下空格键 Space ,进行随机生成

Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题_第6张图片

4、把 Test 挂载到场景中,赋值父物体,和对应预制体

Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题_第7张图片

5、运行,按下空格键Space 随机生成 Item 有时候就会出现不能及时适配更新的情况

Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题_第8张图片

6、进行对应的处理后,这里是在 LateUpdate 进行更新

  • 如果这里比较耗性能,
  • 可以试着在有数据UI变化的时候触发更新若干时间关闭更新
  • 根据需要合理调整即可

Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题_第9张图片

Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题_第10张图片

7、运行场景,不能及时更新布局问题基本解决

Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题_第11张图片

六、案例关键代码


using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

/// 
/// ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题
/// 
public class Test : MonoBehaviour
{
    #region Data

    /// 
    /// ImageGroup 预制体
    /// 
    public GameObject ImageGroup;
    /// 
    /// ImageItem 预制体
    /// 
    public GameObject ImageItem;
    /// 
    /// ParentTran 父物体 ImageGroup
    /// 
    public Transform ParentTran;

    /// 
    /// ImageGroup 列表
    /// 
    List m_ImageGroupGoLst;

    #endregion

    #region Lifecycle function
    /// 
    /// Start is called before the first frame update
    /// 
    void Start()
    {
        Init();
    }

    /// 
    /// Update is called once per frame
    /// 
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space)) {
            GameObject go = GetRandomImageGroup();
            CreateImageItemGameObject(go.transform);
        }
    }

    /// 
    /// LateUpdate is called once per frame
    /// 
    private void LateUpdate()
    {
        // 如果这里比较耗性能,
        // 可以试着在有数据UI变化的时候触发更新若干时间关闭更新
        // 根据需要合理调整即可
        RefreshUICanvas();
    }

    #endregion

    #region Private Function

    /// 
    /// 初始化生成部分队列
    /// 
    void Init() {
        m_ImageGroupGoLst = new List();
        for (int i = 0; i < 2; i++)
        {
            GameObject go = CreateImageGroupGameObject();
            if (i == 1) {
                for (int ii = 0; ii < 4; ii++)
                {
                    CreateImageItemGameObject(go.transform);
                }
            }
            m_ImageGroupGoLst.Add(go);
        }
    }

    /// 
    /// 生成 ImageGroup 实体
    /// 
    /// 
    GameObject CreateImageGroupGameObject() {
        return GameObject.Instantiate(ImageGroup, ParentTran,false);
    }

    /// 
    /// 生成 ImageItem 实体
    /// 
    /// 
    GameObject CreateImageItemGameObject(Transform parent)
    {
        return GameObject.Instantiate(ImageItem, parent, false);
    }

    /// 
    /// 随机获取 生成的 ImageGroup 实体
    /// 
    /// 
    GameObject GetRandomImageGroup() {
        int index = Random.Range(0, m_ImageGroupGoLst.Count);
        GameObject go = m_ImageGroupGoLst[index];
        return go;
    }

    /// 
    /// 刷新 UI  界面
    /// 
    void RefreshUICanvas()
    {
        // 强制刷新
        Canvas.ForceUpdateCanvases();
        // Layout 重建
        LayoutRebuilder.ForceRebuildLayoutImmediate(ParentTran.GetComponent());

        // contentSizeFitter 设置
        ContentSizeFitter contentSizeFitter = ParentTran.GetComponent();
        if (contentSizeFitter != null)
        {
            contentSizeFitter.SetLayoutHorizontal();
            contentSizeFitter.SetLayoutVertical();
        }
    }

    #endregion
}

你可能感兴趣的:(Unity,unity,UI,刷新,布局更新,布局适配)