RecyclerView 应与 stackFromEnd 保持在底部

Posted

技术标签:

【中文标题】RecyclerView 应与 stackFromEnd 保持在底部【英文标题】:RecyclerView should stay at bottom with stackFromEnd 【发布时间】:2021-06-04 09:13:26 【问题描述】:

所以基本上这个问题已经存在于网络上:如何让我的RecyclerView 滚动到底部并显示新项目?几乎总能找到答案:添加stackFromEnd=truereverseLayout=true。就我而言,我尝试了两者的所有可能组合,但它没有奏效。我还看到了这样的解决方案:

    viewAdapter.registerAdapterDataObserver(object : AdapterDataObserver() 
        override fun onItemRangeInserted(positionStart: Int, itemCount: Int) 
            //(layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(viewAdapter.itemCount - 1, 0) or
            //(layoutManager as? LinearLayoutManager)?.smoothScrollToPosition(this@ChatMessagesList, null, viewAdapter.itemCount - 1) or
            //this@ChatMessagesList.smoothScrollToPosition(viewAdapter.itemCount - 1)
        
    )

这也没有帮助我。首先我将描述我的设置,然后描述预期的行为和当前行为:

我有一个如下所示的 xml:

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root_view"
    android:layout_
    android:layout_
    tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">

    <ImageView
        android:id="@+id/backgroundImage"
        android:layout_
        android:layout_
        android:contentDescription="@string/image_image_description"
        android:scaleType="centerCrop"
        android:src="@drawable/chat_background"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <View
        android:id="@+id/overlay"
        android:layout_
        android:layout_
        android:background="#CCFFFFFF"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <app.jooy.messenger.ui.components.generic.chat.chat_header.ChatHeader
        android:id="@+id/chat_header"
        android:layout_
        android:layout_
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <app.jooy.messenger.ui.components.generic.chat.chat_message_list.ChatMessagesList
        android:id="@+id/chat_messages_list"
        android:layout_
        android:layout_
        app:layout_constraintBottom_toTopOf="@id/chat_options_chooser"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/chat_header" />

    <app.jooy.messenger.ui.components.generic.chat.chat_options_chooser.ChatOptionsChooser
        android:id="@+id/chat_options_chooser"
        android:layout_
        android:layout_
        android:visibility="gone"
        app:layout_constraintBottom_toTopOf="@id/chat_emoji_bar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <app.jooy.messenger.ui.components.generic.chat.chat_emoji_bar.ChatEmojiBar
        android:id="@+id/chat_emoji_bar"
        android:layout_
        android:layout_
        android:visibility="gone"
        app:layout_constraintBottom_toTopOf="@id/chat_action_bar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <app.jooy.messenger.ui.components.generic.chat.chat_action_bar.ChatActionBar
        android:id="@+id/chat_action_bar"
        android:layout_
        android:layout_
        android:background="@color/colorActionBarBackground"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</merge>

重要的视图是chat_messages_list RecyclerView,这是我的RecyclerView,其实现如下(为简单起见删除了一些内容):

@ExperimentalCoroutinesApi
@ReactiveComponent
class ChatMessagesList @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ReactiveRecyclerView<ChatMessagesList.ViewState, ChatMessagesList.ViewAction, ChatMessagesList.ViewStructure>(
    ViewState(),
    context,
    attrs,
    defStyleAttr
) 

    private val viewAdapter = ChatMessagesListAdapter()

    init 

        setHasFixedSize(true)
        layoutManager = object : LinearLayoutManager(context) 
            init 
                stackFromEnd = true
            
        
        viewAdapter.registerAdapterDataObserver(object : AdapterDataObserver() 
            override fun onItemRangeInserted(positionStart: Int, itemCount: Int) 
                //(layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(0, 0)
                //(layoutManager as? LinearLayoutManager)?.smoothScrollToPosition(this@ChatMessagesList, null, viewAdapter.itemCount - 1)
                //this@ChatMessagesList.smoothScrollToPosition(0)
            
        )

        registerTypes(viewAdapter)
        adapter = viewAdapter

        addItemDecoration(getDecoration())

        setPadding(0, 6.toPx(), 0, 6.toPx())
        clipToPadding = false

    


此外,我可以告诉你,我的适配器肯定会调用正确的 notifyItemRangeInserted, ... 调用。

预期行为: RecyclerView 应该从屏幕底部的第一个项目开始,并且始终如果添加新项目,则应将其添加到底部并滚动到。最后底部的新项目应该看起来像是从底部推入的。

当前行为: 项目从底部开始堆叠,直到RecyclerView 内的可见空间未满,所有的动画都非常漂亮,但是一旦可见空间已满,项目就会添加到底部但不会滚动到。正如某些人所期望的那样,我想要实现的功能是聊天功能。

补充说明: 如果我将this@ChatMessagesList.smoothScrollToPosition(viewAdapter.itemCount - 1) 添加到onItemRangeInserted 回调中,它会转到底部但没有动画,它只是跳到那里。

【问题讨论】:

【参考方案1】:

所以我自己想通了。我仍然不知道为什么某些策略有效,而有些则根本无效。

我在LinearLayoutManager 上设置了reverseLayout=true(这意味着我必须颠倒列表中的项目),然后我添加了一个onItemRangeInserted 监听器,如下所示:

    layoutManager = object : LinearLayoutManager(context) 
        init 
            reverseLayout = true
        
    
    viewAdapter.registerAdapterDataObserver(object : AdapterDataObserver() 
        override fun onItemRangeInserted(positionStart: Int, itemCount: Int) 
            if (positionStart == 0) 
                layoutManager?.scrollToPosition(0)
            
        
    )

【讨论】:

以上是关于RecyclerView 应与 stackFromEnd 保持在底部的主要内容,如果未能解决你的问题,请参考以下文章

在SQL中,主表应与多列的查找表匹配

Android,调用 API 14 方法,但应与 API 4 保持兼容

在 UItextfield 的右视图上添加一个按钮,文本不应与按钮重叠

哪个图形 API 应与 Azure AD B2C 一起使用

Google Play Console:Instant App APK 的包名称应与应用的包名称相同

将异步协程作为 celery 任务运行