Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

当totalCount数量减少时(比如从50个->10个),调用Refresh会导致item都看不见了,是否考虑这种情况自动刷新到底部 #186

Closed
MonsterZhangChen opened this issue Feb 7, 2025 · 13 comments

Comments

@MonsterZhangChen
Copy link

20250207162709_rec_.mp4
@qiankanglai
Copy link
Owner

qiankanglai commented Feb 7, 2025

这个问题其实和#185 相关,Refresh只用于明确知道只需要刷新数据、不修改布局的情况(这样相比Refill节约性能)

/// <summary>
/// Refresh item data
/// </summary>
public void RefreshCells()

@MonsterZhangChen
Copy link
Author

MonsterZhangChen commented Feb 7, 2025

这个问题其实和#185 相关,Refresh只用于明确知道只需要刷新数据、不修改布局的情况(这样相比Refill节约性能)

///


/// Refresh item data
///

public void RefreshCells()

嗯,那我考虑在业务层处理一下数量的判断,当数量减少且有部分留白时(这块有点复杂T_T),让列表”沉底“

@qiankanglai
Copy link
Owner

我也写过类似的业务代码orz:当个数不够的时候,转而调用RefillCellsFromEnd让列表”沉底“;当个数够的情况下,用GetFirstItem+RefillCells尽量保持位置不变。

这块我也是纠结过库应该怎么处理比较好,毕竟遇到不同项目的需求其实是不太一样的... 所以我目前的设计思路是暴露相对底层的API,但业务代码就要多写几行了。。。
也许封装出几个常见的模式是一个方案? 譬如元素不足时自动沉底或者置顶/元素足够时候固定头或者尾... 细节比较多hhh

@MonsterZhangChen
Copy link
Author

MonsterZhangChen commented Feb 7, 2025

我也写过类似的业务代码orz:当个数不够的时候,转而调用RefillCellsFromEnd让列表”沉底“;当个数够的情况下,用GetFirstItem+RefillCells尽量保持位置不变。

这块我也是纠结过库应该怎么处理比较好,毕竟遇到不同项目的需求其实是不太一样的... 所以我目前的设计思路是暴露相对底层的API,但业务代码就要多写几行了。。。 也许封装出几个常见的模式是一个方案? 譬如元素不足时自动沉底或者置顶/元素足够时候固定头或者尾... 细节比较多hhh

目前公司项目已经深入使用LoopScrollRect了。我之前提的数量较少需要沉底、自动定位(居中、前面或者到视框停止)需求和RefreshCellsRefillCells的统一问题,多个项目都需要用到,都是很朴素的需求。您是LoopScrollRect的作者,业务代码多写几行可以解决这些朴素的需求,而我可能写出更多丑陋而低性能的代码。我觉得封装几个模式挺好的,可以解决这些朴素的需求。

回到”沉底“问题,当个数不够的时候,转而调用RefillCellsFromEnd让列表”沉底“:但是实际个数不够时(50->10),如果本身列表已经滑动到很前面(已显示2到8)时,此时是不需要沉底的,这时怎么判断不需要沉底的情况呢(我可能会在'RefreshCells'后看是否回收了视框内的item,如果回收了就调用RefillCellsFromEnd)。
Image
或者可以的话,是否能贴出您判断的代码给我看一下 :)

@MonsterZhangChen
Copy link
Author

我也写过类似的业务代码orz:当个数不够的时候,转而调用RefillCellsFromEnd让列表”沉底“;当个数够的情况下,用GetFirstItem+RefillCells尽量保持位置不变。

这块我也是纠结过库应该怎么处理比较好,毕竟遇到不同项目的需求其实是不太一样的... 所以我目前的设计思路是暴露相对底层的API,但业务代码就要多写几行了。。。 也许封装出几个常见的模式是一个方案? 譬如元素不足时自动沉底或者置顶/元素足够时候固定头或者尾... 细节比较多hhh

解决:统一问题+解决数量少了的问题

void RefillCellsInternal(int dataCount = -1, int startItem = 0)
{
    bool isEndEmpty = false;
    var lastCount = LoopScrollRect.totalCount;
    var changeCount = dataCount - LoopScrollRect.totalCount;
    if (dataCount >= 0)
        LoopScrollRect.totalCount = dataCount;

    //指定到某一位置,Refill
    if (startItem >= 0)
    {
        LoopScrollRect.RefillCells(startItem);
    }
    //第一次刷新(或者上一次数量为0),必须Refill
    else if (lastCount == 0)
    {
        LoopScrollRect.RefillCells();
    }
    //数量不变时或者增加时,Refresh
    else if(changeCount >= 0)
    {
        LoopScrollRect.RefreshCells();
    }
    //数量减少时
    else
    {
        var lastIdx = LoopScrollRect.GetLastItem(out _);
        if (lastIdx > LoopScrollRect.totalCount - 1)
            isEndEmpty = true;
        //底部留白,从底部重新填充
        if (isEndEmpty)
            LoopScrollRect.RefillCellsFromEnd();
        //底部不留白,保持当前滚动位置
        else
            LoopScrollRect.RefreshCells();
    }
}

@MonsterZhangChen
Copy link
Author

MonsterZhangChen commented Feb 8, 2025

我也写过类似的业务代码orz:当个数不够的时候,转而调用RefillCellsFromEnd让列表”沉底“;当个数够的情况下,用GetFirstItem+RefillCells尽量保持位置不变。

这块我也是纠结过库应该怎么处理比较好,毕竟遇到不同项目的需求其实是不太一样的... 所以我目前的设计思路是暴露相对底层的API,但业务代码就要多写几行了。。。 也许封装出几个常见的模式是一个方案? 譬如元素不足时自动沉底或者置顶/元素足够时候固定头或者尾... 细节比较多hhh

又发现了一出细节问题,即RefillCellsFromEnd并不能事实上真正的”沉底“,而只能保证最后一个Item可以露出来。因此我加入了补丁:

public void RefillCellsFromEnd(int endItem = 0, bool alignStart = false)
{
    if (!Application.isPlaying)
        return;

    itemTypeEnd = reverseDirection ? endItem : totalCount - endItem;
    itemTypeStart = itemTypeEnd;

    if (totalCount >= 0 && itemTypeStart % contentConstraintCount != 0)
    {
        itemTypeStart = (itemTypeStart / contentConstraintCount) * contentConstraintCount;
    }

    ReturnToTempPool(!reverseDirection, m_Content.childCount);

    float sizeToFill = GetAbsDimension(viewRect.rect.size), sizeFilled = 0;
    bool first = true;
    // issue 169: fill last line
    if (itemTypeStart < itemTypeEnd)
    {
        itemTypeEnd = itemTypeStart;
        float size = reverseDirection ? NewItemAtStart(!first) : NewItemAtEnd(!first);
        if (size >= 0)
        {
            first = false;
            sizeFilled += size;
        }
    }

    while (sizeToFill > sizeFilled)
    {
        float size = reverseDirection ? NewItemAtEnd(!first) : NewItemAtStart(!first);
        if (size < 0)
            break;
        first = false;
        sizeFilled += size;
    }

    // refill from start in case not full yet
    while (sizeToFill > sizeFilled)
    {
        float size = reverseDirection ? NewItemAtStart(!first) : NewItemAtEnd(!first);
        if (size < 0)
            break;
        first = false;
        sizeFilled += size;
    }

    Vector2 pos = m_Content.anchoredPosition;
    float dist = alignStart ? 0 : Mathf.Max(0, sizeFilled - sizeToFill);
    if (reverseDirection)
        dist = -dist;
    if (direction == LoopScrollRectDirection.Vertical)
        pos.y = dist;
    else
        pos.x = -dist;
    m_Content.anchoredPosition = pos;
    m_ContentStartPosition = pos;

    ClearTempPool();
    // force build bounds here so scrollbar can access newest bounds
    LayoutRebuilder.ForceRebuildLayoutImmediate(m_Content);
    Canvas.ForceUpdateCanvases();
    UpdateBounds(false);
    UpdateScrollbars(Vector2.zero);
    StopMovement();
    UpdatePrevData();

    //下面补丁代码
    if (direction == LoopScrollRectDirection.Horizontal)
        horizontalNormalizedPosition = 1;
    else
        verticalNormalizedPosition = 1;
}

@qiankanglai
Copy link
Owner

我觉得封装几个模式挺好的,可以解决这些朴素的需求。

这个需求我理解,我琢磨了下准备先多扩展下当前的demo scene,把常见的一些模式都作为demo先提供一份看看反馈~ 等这部分稳定了之后、挪入LoopScrollRect中(主要是加进来的API我想尽可能保持稳定,这样用户升级的时候最好不要遇到breaking change)


解决:统一问题+解决数量少了的问题

和这个函数比较像:先检查changeCount == 0或者GetLastItem()>totalCount-1的情况下,可以直接调用RefreshCells;然后要多一步检查新的totalCount能否填满当前viewport,如果可以就RefillCellsFromEnd、否则还是RefillCells
这块我回头整理下,以sample代码形式先提供一份。


又发现了一出细节问题,即RefillCellsFromEnd并不能事实上真正的”沉底“

我怀疑是因为本身元素比较少导致无法填充满了?我本地测了下这种情况:

  1. totalCount=10的时候,RefillCellsRefillCellsFromEnd都是这个效果,因为元素不够多
    Image
  2. totalCount=11的时候,RefillCellsFromEnd效果没问题
    Image

@MonsterZhangChen
Copy link
Author

MonsterZhangChen commented Feb 11, 2025

这个需求我理解,我琢磨了下准备先多扩展下当前的demo scene,把常见的一些模式都作为demo先提供一份看看反馈~ 等这部分稳定了之后、挪入LoopScrollRect中(主要是加进来的API我想尽可能保持稳定,这样用户升级的时候最好不要遇到breaking change)

好的,期待您的”模式“代码!


我怀疑是因为本身元素比较少导致无法填充满了?我本地测了下这种情况:

你的示例demo里是没有问题的。
我怀疑是因为LoopScrollRect和ViewPort在不在同一个节点下有关,测试完发现和这个无关;
后来您猜怎么着,发现原来是因为我们的UI策划修改了这里Vertical Layout GroupPadding导致的。当top == 0 时,这个问题不再复现。
Image
但是在理想情况下,我仍然希望top(或者其他padding的值)被修改时,仍然可以正常适配RefillFromEnd()。期望在未来可以得到解决。

@qiankanglai
Copy link
Owner

master提交了08744f8 ,处理有padding情况下的RefillFromEnd()
Image
其他几个 Horizontal/Vertical * 不同padding的组合我本地测了下也ok了,有空可以试一下最新的master分支~

sample代码我尽快整理好之后提交

@MonsterZhangChen
Copy link
Author

MonsterZhangChen commented Feb 12, 2025

master提交了08744f8 ,处理有padding情况下的RefillFromEnd()

最新分支在padding情况下测试过了,没什么问题。


我观察到995bcb6 已经增加了一个“滑到中间“的模式,我仍觉得应该有一个“滑到视框停止”的模式orz(并非催更,而是怕你没注意到),并注意一下边界问题处理。

@qiankanglai
Copy link
Owner

qiankanglai commented Feb 12, 2025

应该有一个“滑到视框停止”的模式orz

这个有看到,其实我参考你的代码试了下,ScrollToCell比较好实现、但是ScrollToCellWithinTime不太好弄(距离不是那么好算...) 所以我暂时还没提交。
不过想了想可以先实现一个前者+后者报个unimplemented assert先 😏

@MonsterZhangChen
Copy link
Author

MonsterZhangChen commented Feb 12, 2025

这个有看到,其实我参考你的代码试了下,ScrollToCell比较好实现、但是ScrollToCellWithinTime不太好弄(距离不是那么好算...) 所以我暂时还没提交。 不过想了想可以先实现一个前者+后者报个unimplemented assert先 😏

哈哈,我的代码很丑陋,为了实现需求硬拼的:

  • 为了让目标位置出现,先RefillCells(目标索引)
  • 中间计算偏移值;
  • 最后可能会出现出框问题 (可能加入Padding就不需要了,我也忘了之前怎么出的了,所以加入的补丁) ,因此加入了verticalNormalizedPosition >= 1||verticalNormalizedPosition < 0的判定,如果出框还需要RefillCells()或者RefillCellsFromEnd();
    因此最多可能出现两次Refills()

期待你的优雅实现 😄

@qiankanglai
Copy link
Owner

ScrollToCell滑动模式扩展的讨论还是放issue #170

扩展sample示例,我新起了个issue #187

这个issue我先关闭了~不然稍微有点乱orz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants