簡體   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