目录
一、前言
二、效果
三、代码解析
EnhancedScrollRect.cs 解析
Start 方法
HandleArrowVisibility 方法
EnhancedScrollRectEditor.cs 解析
OnEnable 方法
OnInspectorGUI 方法
四、完整代码
EnhancedScrollRect.cs
EnhancedScrollRectEditor.cs
五、总结
Demo链接:
https://download.csdn.net/download/qq_41973169/89428682 https://download.csdn.net/download/qq_41973169/89428682
https://download.csdn.net/download/qq_41973169/89428682
一、前言
Unity版本:2022.3.x
在Unity游戏开发中,ScrollRect是一个常用的UI组件,用于实现可滚动的内容区域。然而,默认的ScrollRect组件并不提供对内容边缘的指示功能,例如在内容可以向左或向右继续滚动时显示箭头。为了解决这个问题,我们扩展了ScrollRect组件,增加了两个箭头指示器(leftArrow和rightArrow),用于提示用户当前内容是否可以继续滚动。这两个脚本分别是EnhancedScrollRect.cs和EnhancedScrollRectEditor.cs。
二、效果

三、代码解析
EnhancedScrollRect.cs 解析
Start 方法
在Start方法中,我们首先调用了基类的Start方法,然后添加了一个监听器,当ScrollRect的值改变时触发HandleArrowVisibility方法。接着,我们检查内容是否有ContentSizeFitter组件,如果有的话,我们强制立即重建布局以确保内容的尺寸正确。最后,我们手动调用一次HandleArrowVisibility方法,以初始化箭头的可见性。
protected override void Start()
{
    base.Start();
    onValueChanged.AddListener(HandleArrowVisibility);
    var contentSizeFitter = content.GetComponent<ContentSizeFitter>();
    if (!contentSizeFitter.IsUnityNull())
    {
        LayoutRebuilder.ForceRebuildLayoutImmediate(content);
    }
    HandleArrowVisibility(new Vector2());
}
HandleArrowVisibility 方法
HandleArrowVisibility方法主要用于根据内容的位置来更新箭头的可见性。首先,我们检查各种关键组件是否为null,如果有任何一个为null,则直接返回。然后,我们计算内容的起始位置和结束位置,并根据这些位置判断是否应该显示左右箭头。
private void HandleArrowVisibility(Vector2 position)
{
    if (content.IsUnityNull() || viewport.IsUnityNull() || leftArrow.IsUnityNull() || rightArrow.IsUnityNull())
    {
        return;
    }
    var contentStart = content.anchoredPosition.x;
    var contentEnd = contentStart + content.rect.width - viewport.rect.width;
    leftArrow.gameObject.SetActive(contentStart < -1.0f);
    rightArrow.gameObject.SetActive(contentEnd > 1.0f);
}
EnhancedScrollRectEditor.cs 解析
OnEnable 方法
在OnEnable方法中,我们首先调用基类的OnEnable方法,然后使用serializedObject.FindProperty方法查找leftArrow和rightArrow属性,并将结果存储在相应的变量中。
protected override void OnEnable()
{
    base.OnEnable();
    _leftArrow = serializedObject.FindProperty("leftArrow");
    _rightArrow = serializedObject.FindProperty("rightArrow");
}
OnInspectorGUI 方法
在OnInspectorGUI方法中,我们首先调用基类的OnInspectorGUI方法,然后更新序列化对象的状态。接着,我们使用EditorGUILayout.PropertyField方法绘制leftArrow和rightArrow属性的编辑字段。最后,我们应用修改并在检测到GUI发生变化时,将目标对象标记为脏,以确保更改被保存。
public override void OnInspectorGUI()
{
    base.OnInspectorGUI();
    serializedObject.Update();
    EditorGUILayout.PropertyField(_leftArrow);
    EditorGUILayout.PropertyField(_rightArrow);
    serializedObject.ApplyModifiedProperties();
    if (GUI.changed)
    {
        EditorUtility.SetDirty(target);
    }
}
四、完整代码
EnhancedScrollRect.cs
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;
public class EnhancedScrollRect : ScrollRect
{
    [SerializeField] private RectTransform leftArrow;
    [SerializeField] private RectTransform rightArrow;
    protected override void Start()
    {
        base.Start();
        onValueChanged.AddListener(HandleArrowVisibility);
        var contentSizeFitter = content.GetComponent<ContentSizeFitter>();
        if (!contentSizeFitter.IsUnityNull())
        {
            LayoutRebuilder.ForceRebuildLayoutImmediate(content);
        }
        HandleArrowVisibility(new Vector2());
    }
    private void HandleArrowVisibility(Vector2 position)
    {
        if (content.IsUnityNull() || viewport.IsUnityNull() || leftArrow.IsUnityNull() || rightArrow.IsUnityNull())
        {
            return;
        }
        var contentStart = content.anchoredPosition.x;
        var contentEnd = contentStart + content.rect.width - viewport.rect.width;
        leftArrow.gameObject.SetActive(contentStart < -1.0f);
        rightArrow.gameObject.SetActive(contentEnd > 1.0f);
    }
}EnhancedScrollRectEditor.cs
using UnityEditor;
using UnityEditor.UI;
using UnityEngine;
[CustomEditor(typeof(EnhancedScrollRect), true)]
[CanEditMultipleObjects]
public class EnhancedScrollRectEditor : ScrollRectEditor
{
    private SerializedProperty _leftArrow;
    private SerializedProperty _rightArrow;
    protected override void OnEnable()
    {
        base.OnEnable();
        _leftArrow = serializedObject.FindProperty("leftArrow");
        _rightArrow = serializedObject.FindProperty("rightArrow");
    }
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        serializedObject.Update();
        EditorGUILayout.PropertyField(_leftArrow);
        EditorGUILayout.PropertyField(_rightArrow);
        serializedObject.ApplyModifiedProperties();
        if (GUI.changed)
        {
            EditorUtility.SetDirty(target);
        }
    }
}五、总结
通过这两个脚本,我们扩展了Unity的ScrollRect组件,使其能够根据内容的滚动状态显示左右箭头,提示用户内容可以继续滚动。此外,我们还自定义了编辑器,可以在Inspector窗口中方便地设置左右箭头的引用。这种扩展方法既保留了原有组件的所有功能,又增强了用户体验,是一种推荐的组件扩展方式。


















