当前位置: 首页 > 知识库问答 >
问题:

如何将Diffutil.Callback与具有页眉和页脚的RecycerView一起使用?

任绪
2023-03-14

我在一个应用程序的工作,有一个回收视图,你可以向上和向下滚动,但你希望。

数据项是从服务器加载的,所以如果你要到达底部或顶部,应用程序会在那里显示新的数据。

为了避免奇怪的滚动行为并保持在当前项上,我使用'diffutil.callback',重写'get oldlistsize'、'get newlistsize'、'are itemsthesame'、'are contentsthesame'。

由于Internet连接可能会很慢,在这个RecycerView中有一个页眉项和页脚项,它们只是有一个特殊的进度视图,以显示您已经到达边缘,它将很快被加载。

页眉和页脚始终存在于列表中,它们不从服务器接收。它纯粹是UI的一部分,只是为了显示即将加载的东西。

事情是,就像其他项一样,它需要由diffutil.callback处理,所以对于areItemsTheSameareContentsTheSame,如果旧页眉是新页眉,旧页脚是新页脚,我只返回true:

    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        val oldItem = oldItems[oldItemPosition]
        val newItem = newItems[newItemPosition]
        when {
            oldItem.itemType != newItem.itemType -> return false
            oldItem.itemType == ItemType.TYPE_FOOTER || oldItem.itemType == AgendaItem.TYPE_HEADER -> return true
            ...
        }
    }

    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        val oldItem = oldItems[oldItemPosition]
        val newItem = newItems[newItemPosition]
        return when {
            oldItem.itemType == ItemType.TYPE_FOOTER || oldItem.itemType == ItemType.TYPE_HEADER -> true
            ...
        }
    }
}
  • 前:页眉、0、1、2、3、页脚
  • 后:页眉,-3,-2,-1,0,1,2,3,页脚

因此,如果您停留在标题上,服务器向您发送了新列表,您仍然可以看到标题,并在新项下面,而看不到旧项。它为你滚动而不是停留在相同的位置上。

这里有一张草图显示了这个问题。黑色矩形显示列表的可见部分。

我在想一些变通办法,但每一种都有自己的缺点:

>

  • 一个NestedScrollView(或RecycerView),它将把页眉和页脚以及RecycerView放在中间,但这可能会导致一些滚动问题,特别是由于我已经有了一个依赖于RecycerView的复杂布局(视图的折叠等)。

    也许在普通项目的布局中,我也可以把页眉和页脚的布局(或者只是页眉,因为这个是有问题的)。但这对性能来说是一件坏事,因为它徒劳地膨胀了额外的视图。另外,它要求我切换隐藏和查看新视图。

    是否有方法告诉diffutil.callback:“这些项(页眉和页脚)不是要滚动到的实际项,而这些项(实际数据项)应该是”?

  • 共有1个答案

    施英哲
    2023-03-14

    我会试着解释一下我认为解决你问题的办法:

    步骤1:移除页脚和页眉视图的所有代码。

    步骤2:添加这些方法,根据用户滚动方向在adapter中添加和移除虚拟模型项:

    /**
     * Adds loader item in the adapter based on the given boolean.
     */
    public void addLoader(boolean isHeader) {
        if (!isLoading()) {
            ArrayList<Model> dataList = new ArrayList<>(this.oldDataList);
            if(isHeader) {
               questions.add(0, getProgressModel());
            else {
               questions.add(getProgressModel());
            setData(dataList);
        }
    }
    
    /**
     * Removes loader item from the UI.
     */
    public void removeLoader() {
        if (isLoading() && !dataList.isEmpty()) {
            ArrayList<Model> dataList = new ArrayList<>(this.oldDataList);
            dataList.remove(getDummyModel());
            setData(questions);
        }
    }
    
    public MessageDetail getChatItem() {
        return new Model(0, 0, 0, "", "", "")); // Here the first value is id which is set as zero.
    }
    

    下面是适配器逻辑的其余部分,您需要确定该项是加载器项还是实际数据项:

    @Override
    public int getItemViewType(int position) {
        return dataList.get(position).getId() == 0 ? StaticConstants.ItemViewTypes.PROGRESS : StaticConstants.ItemViewTypes.CONTENT;
    }
    

    根据视图类型,可以在适配器中添加进度条视图持有人。

    步骤3:在数据加载逻辑中使用这些方法:

    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (dy < 0) { //This is top scroll, so add a loader as the header.
                    recyclerViewAdapter.addLoader(true);
                    LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
                    if (!recyclerViewAdapter.isLoading(true)) {
                        if (linearLayoutManager.findFirstCompletelyVisibleItemPosition() <= 2) {
                            callFetchDataApi();
                        }
                    }
                }
            } else {
                if (!recyclerViewAdapter.isLoading(false)) {
                    if (linearLayoutManager.findLastCompletelyVisibleItemPosition() >= linearLayoutManager.getItemCount() - 2) {
                        callFetchDataApi();
                    }
                }
        });
    
    private void onGeneralApiSuccess(ResponseModel responseModel) {
        myStreamsDashboardAdapter.removeLoader();
        if (responseModel.getStatus().equals(SUCCESS)) {
                // Manage your pagination and other data loading logic here.
                dataList.addAll(responseModel.getDataList());
                recyclerViewAdapter.setData(dataList);
            }
    }
    
    public boolean isLoading(boolean isFromHeader) {
        if (isFromHeader) {
            return dataList.isEmpty() || dataList.get(0).getId() == 0;
        } else {
            return dataList.isEmpty() || dataList.get(dataList.size() -1).getId() == 0;
        }
    }
    
     类似资料:
    • 也许以前有人问过这个问题,但我似乎找不到一个准确的答案或解决办法。我开始使用RecycerView,并使用LinearLayoutManager实现了它。现在,我想添加自定义的页眉和页脚项,这些项不同于RecycerView中的其他项。页眉和页脚不应该粘,我希望他们滚动与其余的项目。有人能指出一些例子如何做到这一点或只是分享想法。我会非常感激的。THX

    • 我花了一点时间试图找到一种方法来向添加一个标头,但没有成功。 这是我目前得到的: 似乎是处理项的对象。由于我找不到任何方法,所以我决定使用的方法,并在第一个位置添加header视图以充当header。 这就是事情变得更丑陋的地方: 在创建活动的不同时刻,尝试调用的几次(也尝试在设置好所有内容后添加视图,甚至适配器的数据)之后,我意识到我不知道这是否是正确的方法(而且看起来并不正确)。 PS:另外,

    • 问题内容: 如何使用itext从html源向pdf添加标头? 当前,我们扩展了PdfPageEventHelper并覆盖了这些方法。工作正常,但是当我进入2个以上页面时,它将引发RuntimeWorkerException。 问题答案: 通常, 禁止 在事件中添加内容。这是 禁止 添加内容到的对象。您应该使用而 不是 文档在方法中添加页眉和页脚。此外:通过一遍又一遍地解析HTML,您正在浪费大量C

    • 使用iTextSharp,您可以通过将事件附加到PDF来向PDF添加页眉/页脚,如本SO答案中所述:https://stackoverflow.com/a/19004392 我怎样才能用 iText 7 做同样的事情? 这个链接有Java代码示例,但看起来不像它使用的页面事件。

    • 问题内容: 我真的不喜欢在每个控制器中编写代码: 是否可以这样做,将自动包含页眉和页脚,如果需要更改它,我们也可以这样做吗?你怎么处理?还是您认为这不是问题?谢谢。 问题答案: 这是我的工作: 对于CI 3.x: 然后,在您的控制器中,这就是您要做的一切:

    • 我正在开发一个Spring MVC应用程序,它将FreeMarker用于我的视图。 我对FreeMarker非常陌生,我有以下问题:在我的projct中,有3个文件必须被组装到一个页面中。 所以我有: 1) 标题。ftl表示我所有页面的标题,类似于: 2)footer.ftl代表我所有页面的页脚: 3) 然后我有了我的特定页面(名为myPage.ftl),它只表示内容,如下所示: 标题。ftl和页