繁体   English   中英

添加新消息时 RecyclerView 向下滚动

[英]The RecyclerView scrolls to downward when a new message is added

我有一个聊天应用程序。 我使用了 RecyclerView,并将 stackFromEnd true 和 reverseLayout false 设置为 LinearlayoutManager。 当在底部添加新消息时,我的意思是在列表末尾,recyclerView 开始自动向下滚动而不是向上滚动。 适配器由 notifyItemInserted() 通知。

预期行为:将新消息添加到列表时,它应该滚动到底部或向上。 任何帮助表示赞赏。 谢谢,

这是适配器:


class ChatMessagesListAdapter(
    var chatMessages: ChatMessagesDataModel
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    companion object {
        // we set the header as 0 so we can add more types to the ConversationItem enum
        private const val ITEM_TYPE_HEADER = 0
    }


    override fun getItemViewType(position: Int): Int {
        return when {
            chatMessages.hasMoreItems -> chatMessages.messages[position].getItemType()
            position == 0 -> ITEM_TYPE_HEADER
            else -> chatMessages.messages[position - 1].getItemType()
        }
    }

    // return +1 to draw the header if there are no items to load
    override fun getItemCount() = chatMessages.messages.size + getContentIndex()

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val inflater = LayoutInflater.from(parent.context)

        return when (viewType) {
            ITEM_TYPE_HEADER -> {
                MessagesHeaderViewHolder(inflater.inflate(R.layout.item_chat_messages_header,
                    parent, false))
            }

            ChatItemDataModel.TYPE_ADDED_USER,
            ChatItemDataModel.TYPE_VIDEO_NOTIFICATION -> {
                MessagesInfoViewHolder(inflater.inflate(R.layout.item_chat_messages_info,
                    parent, false))
            }
            else -> {
                MessagesMessageViewHolder(inflater.inflate(R.layout.item_chat_messages_message,
                    parent, false))
            }
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        (holder as? MessagesHeaderViewHolder)?.bind()
        (holder as? MessagesMessageViewHolder)?.bind(position, chatMessages.messages[getRealPosition(position)])
        (holder as? MessagesInfoViewHolder)?.bind(chatMessages.messages[getRealPosition(position)])
    }

    private fun getContentIndex() = if (chatMessages.hasMoreItems) {
        0
    } else {
        1
    }

    private fun getRealPosition(position: Int) = if (chatMessages.hasMoreItems) {
        position
    } else {
        position - 1
    }

    private fun notifyChanges() {
        if (chatMessages.numberOfItemsInserted == chatMessages.messages.size || isPreview ||
            chatMessages.hadPendingMessages) {
            notifyDataSetChanged()
        } else {
            // +1 because of the header
            notifyItemRangeInserted(chatMessages.insertionIndex + getContentIndex(),
                chatMessages.numberOfItemsInserted)
        }
    }

    fun updateConversation(newChatMessages: ChatMessagesDataModel) {
        chatMessages = newChatMessages
        notifyChanges()
    }

    fun updateMessage(newMessage: ChatItemDataModel, isRemote: Boolean) {
        if (newMessage.message.hashIdentifier.isNullOrBlank()) {
            addNewMessage(newMessage)
            return
        }
        val messageIndex = chatMessages.messages.indexOfFirst { it.message.hashIdentifier == newMessage.message.hashIdentifier }

        if (messageIndex != -1) {
            val localMessage =  chatMessages.messages[messageIndex]
            chatMessages.messages[messageIndex] = newMessage

            if (failedMessages.contains(localMessage.message.sid)) {
                if (isRemote) {
                    failedMessages.remove(localMessage.message.sid)
                }
                notifyItemChanged(messageIndex + getContentIndex())
            }
        }
        else {
            addNewMessage(newMessage)
        }
    }

    private fun addNewMessage(newMessage: ChatItemDataModel) {
        val oldCount = chatMessages.messages.size
        chatMessages.messages.add(newMessage)
        notifyItemInserted(oldCount + getContentIndex())
    }

    fun addLocalMessage(
        sharedPrefsStorage: SharedPrefsStorage,
        message: String, imageUri: Uri?, hashIdentifier: String
    ) {
        val userMessage = UserMessage(messageBody = message, firstName = sharedPrefsStorage.firstName,
            lastName = sharedPrefsStorage.lastName, isFromLoggedUser = true, imageUri = imageUri,
            hashIdentifier = hashIdentifier, files = null, reactions = null)

        val newMessage = ChatItemDataModel(userMessage, sharedPrefsStorage.profileImageUrl, sharedPrefsStorage.userId.toString())
        val oldCount = chatMessages.messages.size
        chatMessages.messages.add(newMessage)
        notifyItemRangeInserted(oldCount + getContentIndex(), 1)
    }
           ....
}

这是片段:

class ChatRoomMessagesFragment : Fragment() {

    @Inject
    lateinit var sharedPrefsStorage: SharedPrefsStorage

    private var adapter: ChatMessagesListAdapter? = null

           ......
override fun onMessagesReceived(chatMessages: ChatMessagesDataModel) {
        if (adapter == null) {
            adapter = ChatMessagesListAdapter(chatMessages)

            adapter?.setHasStableIds(true)
            binding.recyclerview.adapter = adapter
        } else {
            adapter?.updateConversation(chatMessages)
        }
    }

override fun onUserMessageRetrieved(newMessage: ChatItemDataModel, isRemote: Boolean) {
        adapter?.updateMessage(newMessage, isRemote)
    }

    private fun setupUI() {

        binding.apply {

            recyclerview.apply {
                layoutManager = LinearLayoutManager(requireContext()).apply {
                    reverseLayout = false
                    stackFromEnd = true
                    addOnScrollListener(object : RecyclerView.OnScrollListener() {
                        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                            super.onScrolled(recyclerView, dx, dy)
                            showScrollToBottomButtonIfNeeded(findFirstCompletelyVisibleItemPosition())
                        }
                    })
                }

                addOnScrollListener(object : PaginationScrollListener(
                    layoutManager as LinearLayoutManager,
                    ScrollDirection.BOTTOM
                ) {

                    override fun loadMoreItems() {
                        presenter.loadMoreMessages()
                    }

                    override fun isLastPage() = !presenter.hasMoreItems()

                    override fun isLoading() = presenter.isLoadingInProgress()
                })

                // prevent the list from scrolling when the keyboard opens
                addOnLayoutChangeListener { _, _, _, _, bottom, _, _, _, oldBottom ->
                    if (bottom < oldBottom) {
                        scrollChatToBottom()
                    }
                }
            }
       ....
                adapter?.addLocalMessage(sharedPrefsStorage, message, imageUri, hashIdentifier)
                presenter.sendMessage("", message, imageUri, hashIdentifier)
}

 private fun scrollChatToBottom() {
        binding.apply {
            recyclerview.post {
                recyclerview.smoothScrollToPosition(recyclerview.adapter?.itemCount ?: 0)
            }
        }
    }



}

您可以使用下面的代码以编程方式向下滚动

recyclerView.scrollToPosition(chatList.size() - 1);

如果上述方法不起作用,您也可以尝试另一种解决方案

setStackFromEnd(true) or setReverseLayout(true)

假设您的适配器由称为消息的对象填充,并且您有一个名为messageList的数据源(可能是一个集合等),它被传递给您的适配器。 你可以这样做:

        int position = messageList.size() > 0 ? (messageList.size()-1) : 0;
        mRecyclerView.smoothScrollToPosition(position);

只需确保您的适配器不是 null 并且实际上从 messageList 获取数据,因为这是在您的活动/片段 class 中而不是在适配器 class 中。

 yourAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
            override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
                super.onItemRangeInserted(positionStart, itemCount)
                this@EventDetails.binding.rvData.scrollToPosition(positionStart)
            }

            override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
                super.onItemRangeChanged(positionStart, itemCount)
                this@EventDetails.binding.rvData.scrollToPosition(positionStart)
            }
        })

我认为第一种方法可以使用其中一种方法,请尝试避免您自己使用 DiffUtils 或 ListAdapter(回收器视图的扩展)调用 notifyDataSetChange() 或 intemChange()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM