Unity ScrollView滚动到指定item所在位置

通过扩展系统自带ScrollRect新增ScrollToItem 方法实现。

Talk is cheap. Show me the code!

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

public static class ScrollRectExtensions
{
    // ScrollRect滚动到指定位置
    public static void ScrollToItem(this ScrollRect scrollRect, int itemIndex, float duration = 0f)
    {
        // 延迟执行,避免列表还没有准备好时执行。
        scrollRect.StartCoroutine(ScrollToItemCoroutine(scrollRect, itemIndex, duration));
    }

    private static IEnumerator ScrollToItemCoroutine(ScrollRect scrollRect, int itemIndex, float duration = 0f)
    {
        // 等待列表准备好。
        yield return new WaitForFixedUpdate(); // 等待一次物理更新,确保列表已准备好。
        float normalizedPosition = itemIndex / (float)(scrollRect.content.childCount - 1);
        Vector2 targetPosition = new Vector2(scrollRect.horizontalNormalizedPosition, scrollRect.verticalNormalizedPosition);

        if (scrollRect.horizontal)
        {
            targetPosition.x = CalculateHorizontalPosition(scrollRect, normalizedPosition);
        }
        else if (scrollRect.vertical)
        {
            targetPosition.y = CalculateVerticalPosition(scrollRect, normalizedPosition);
        }

        Vector2 startPosition = new Vector2(scrollRect.horizontalNormalizedPosition, scrollRect.verticalNormalizedPosition);
        float elapsedTime = 0f;

        while (elapsedTime < duration)
        {
            float normalizedTime = elapsedTime / duration;
            scrollRect.normalizedPosition = Vector2.Lerp(startPosition, targetPosition, normalizedTime);
            elapsedTime += Time.fixedDeltaTime;
            yield return null; // 等待下一帧。
        }

        scrollRect.normalizedPosition = targetPosition;
    }

private static float CalculateHorizontalPosition(ScrollRect scrollRect, float normalizedPosition)
{
    float viewportWidth = scrollRect.viewport.rect.width;
    float contentWidth = scrollRect.content.sizeDelta.x;
    float paddingWidth = scrollRect.padding().left + scrollRect.padding().right;
    float spacingWidth = (scrollRect.content.childCount - 1) * scrollRect.spacing();
    float totalWidth = contentWidth + paddingWidth + spacingWidth;
    float scrollWidth = totalWidth - viewportWidth;
    float scrollPosition = normalizedPosition * scrollWidth;
    float normalizedScrollPosition = scrollPosition / scrollWidth;
    return normalizedScrollPosition;
}

private static float CalculateVerticalPosition(ScrollRect scrollRect, float normalizedPosition)
{
    float viewportHeight = scrollRect.viewport.rect.height;
    float contentHeight = scrollRect.content.sizeDelta.y;
    float paddingHeight = scrollRect.padding().top + scrollRect.padding().bottom;
    float spacingHeight = (scrollRect.content.childCount - 1) * scrollRect.spacing();
    float totalHeight = contentHeight + paddingHeight + spacingHeight;
    // Debug.Log("contentHeight: " + contentHeight + " paddingHeight: " + paddingHeight + " spacingHeight: " + spacingHeight + " totalHeight: " + totalHeight + " viewportHeight: " + viewportHeight);
    float scrollHeight = totalHeight - viewportHeight;
    float scrollPosition = normalizedPosition * scrollHeight;
    float normalizedScrollPosition = scrollPosition / scrollHeight;
    return normalizedScrollPosition;
}

    // 根据水平或垂直组件获取spacing
    public static float spacing(this ScrollRect scrollRect){
        if(scrollRect.horizontal){
            return scrollRect.content.GetComponent<HorizontalLayoutGroup>().spacing; // 水平布局的spacing是水平方向的间距。
        }else if(scrollRect.vertical){
            return scrollRect.content.GetComponent<VerticalLayoutGroup>().spacing; // 垂直布局的spacing是垂直方向的间距。
        }
        return 0f;
    }

    // 根据水平或垂直组件获取padding
    public static RectOffset padding(this ScrollRect scrollRect){
        if(scrollRect.horizontal){
            return scrollRect.content.GetComponent<HorizontalLayoutGroup>().padding; // 水平布局的padding是水平方向的间距。
        }else if(scrollRect.vertical){
            return scrollRect.content.GetComponent<VerticalLayoutGroup>().padding; // 垂直布局的padding是垂直方向的间距。
        }
        return new RectOffset(0,0,0,0); // 如果没有水平或垂直组件,则返回默认的RectOffset。
    }
}

调用方式:

int itemIndex = 5; // item索引
float scrollTime = 1f; // 滚动时间(可不传,默认为0)
scrollRect.ScrollToItem(itemIndex, scrollTime);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Unity中的ScrollView是一个常用的UI组件,它允许我们在可滚动的区域内呈现大量的内容。ScrollView的子项大小可以被控制,但是如何让它们的大小自动决定? 实现此功能的最常用方法是使用Layout组件,特别是HorizontalLayoutGroup和VerticalLayoutGroup。当一个Layout组件与ScrollView的Content GameObject关联时,它可以自动为每个子项调整大小和位置。 使用Layout组件的过程是: 1. 在ScrollView的Content GameObject中添加一个HorizontalLayoutGroup或VerticalLayoutGroup组件。 2. 在Layout组件中设置Spacing和Child Alignment等属性。 3. 把子项添加到Content GameObject中。 4. 在子项的RectTransform组件中设置大小和位置。 这样,在ScrollView滚动时,子项的大小和位置将自动调整以适应Content GameObject的大小。 需要注意的是,如果子项中有其他布局组件,比如Grid Layout Group,它们的优先级可能高于Layout组件。这时需要调整它们的属性,以确保Layout组件能正常工作。 总之,使用Layout组件可以使ScrollView的子项大小自动调整,简化UI制作流程,提高开发效率。 ### 回答2: Unity中的ScrollView组件是一种常用的UI控件,常用于显示大量的内容时。ScrollView会在其中添加多个子组件,每个子组件代表着一个列表项,因此列表项的尺寸是非常重要的,它直接影响着整个ScrollView的显示效果。 ScrollView中的子组件可以通过设置宽度和高度来定义它们的尺寸,但对于大规模数据的情况下,手动设置所有子组件的大小并不方便。如果我们想要实现尺寸自动的效果,可以使用GridLayout Group组件。 GridLayout Group组件是用于在表格布局中自动排列子组件的组件。它可以按照指定的行和列数,自动计算每个子组件的尺寸,并以表格的形式排列它们。我们可以选择使用它来控制ScrollView中子组件的尺寸。 具体实现方法是,将GridLayout Group组件添加到ScrollView的Content子对象上,然后在属性面板中设置行列数、边距、间距等参数,最后在Content子对象中放置子组件即可。 这样做有以下优点:不需要手动设置每个子组件的大小,可以通过设置行列数灵活控制列表项个数,同时也可以兼顾ScrollView的性能和效果。 总之,Unity ScrollView的子组件尺寸自动可以通过使用GridLayout Group组件来实现,这种方法省去了手动设置每个子组件大小的麻烦,而且效果还不错。 ### 回答3: Unity中的ScrollView作为UI控件常常用来显示大量数据,如列表、图库、日历等。然而,ScrollViewItem的尺寸问题一直是很多开发者头疼的问题。在实际开发中,不同的Item的尺寸不同,而ScrollView必须要自适应Item的尺寸。 如果要实现ScrollViewItem尺寸自动适应的效果,可以掌握以下两种方式: 1. 使用GridLayout Group:GridLayout Group是一种布局组件,可以自动排列UI元素。通过设置它的Cell Size属性,就可以控制Item的尺寸。GridLayout Group还支持打开Child Force Expand属性后,自动拉伸Item的大小适应父容器。 2. 手动设置Canvas Scaler的缩放比例:Canvas Scaler是一种自适应UI尺寸的组件,它可以根据不同的屏幕分辨率,在不同的设备上显示出相同的UI效果。因此,我们可以通过在Canvas Scaler中设置Match Width Or Height选项,然后手动设置Reference Resolution和Screen Match Mode的值,以达到自动适应Item的尺寸效果。 总之,在项目中,我们应该根据不同的需求和UI设计,灵活运用Unity的控件和组件,以达到更好的用户体验和UI效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值