How to save current scroll position for RecyclerView on item deletes?

Advertisements

I have a chat RecyclerView, I want to prevent RecyclerView from auto scrolling to last item on item deleted. I want to make the smooth scroll only on activity starts.
Below, a code samples for setting up the RecyclerView and the message deleting process.
If anyone can help me. Thank you…

Setting RecyclerView

private fun setRecyclerView() {
        val linearLayoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
        linearLayoutManager.stackFromEnd = true
        recyclerView.layoutManager = linearLayoutManager
        //recyclerView.setHasFixedSize(true)
        recyclerView.itemAnimator = DefaultItemAnimator()
        adapter = SingleChatAdapter(this, messages, senderRoom!!, receiverRoom!!, {num ->
            incDecNum(num)
        }) {show, hide ->
            showChosenToolbar(show, hide)
        }
        recyclerView.adapter = adapter
    }

Delete Message

private fun delBtn() {
        val delS = containerView?.findViewById<View>(R.id.deleteSelections)
        delS?.setOnClickListener {
            val senderDel = LayoutInflater.from(this).inflate(R.layout.sender_deletes, null)
            val receiverDel = LayoutInflater.from(this).inflate(R.layout.receiver_deletes, null)
            for (msg: SingleChatData in messages) {
                if (msg.selected && msg.senderId == auth.uid) {
                    val deleteDialog = AlertDialog.Builder(this).setView(senderDel)
                        .setTitle("Delete This message")
                    val myDialog = deleteDialog?.show()
                    senderDel.deleteForMeS.setOnClickListener {
                        deleteForMe()
                        myDialog?.dismiss()
                        showChosenToolbar(false, true)
                        chosenNumTxt.text = "0"
                        adapter.notifyDataSetChanged()
                    }
                    senderDel.deleteForAllS.setOnClickListener {
                        deleteForAll()
                        myDialog?.dismiss()
                        showChosenToolbar(false, true)
                        chosenNumTxt.text = "0"
                        adapter.notifyDataSetChanged()
                    }
                    senderDel.cancelDeletionS.setOnClickListener { myDialog?.dismiss() }
                } else if (msg.selected && msg.senderId != auth.uid) {
                    val deleteDialog = AlertDialog.Builder(this).setView(receiverDel)
                        .setTitle("Delete This message")
                    val myDialog = deleteDialog?.show()
                    receiverDel.deleteForMeR.setOnClickListener {
                        deleteForMe()
                        myDialog?.dismiss()
                        showChosenToolbar(false, true)
                        chosenNumTxt.text = "0"
                        adapter.notifyDataSetChanged()
                    }
                    receiverDel.cancelDeletionR.setOnClickListener { myDialog?.dismiss() }
                }
            }
        }
    }

Add data to adapter

private fun setDataToAdapter(){
        firestore.collection("Chats").document(senderRoom!!).collection("messages").orderBy("time")
            .addSnapshotListener { value, _ ->
                if (!value?.isEmpty!!) {
                    messages.clear()
                    for (document in value.documents) {
                        val coMsg = document.data?.get("message").toString()
                        val coId = document.data?.get("senderId").toString()
                        val msgId = document.id
                        //Using Date
                        // Get Calendar
                        val calendarMap = document.data?.get("date") as Map<*, *>
                        val curTimestamp = calendarMap["time"] as com.google.firebase.Timestamp
                        val millis = curTimestamp.seconds * 1000 + curTimestamp.nanoseconds / 1000000
                        val ss = SimpleDateFormat("hh:mm a", Locale.getDefault())
                        val netCal = Date(millis)
                        val msgTime = ss.format(netCal).toString()
                        // Get Day
                        val chatDay = document.data?.get("day").toString()
                        // Get Full Date
                        val fullDateStamp = calendarMap["time"] as com.google.firebase.Timestamp
                        val dateInMillis = fullDateStamp.seconds * 1000 + fullDateStamp.nanoseconds / 1000
                        val sdf = SimpleDateFormat("MM/dd/yyyy", Locale.getDefault())
                        val netAllDate = Date(dateInMillis)
                        val fullDate = sdf.format(netAllDate).toString()
                        val type = document.data?.get("type").toString()
                        val coMsgT = SingleChatData(coMsg, coId, msgId, msgTime, chatDay, fullDate, type)
                        messages.add(coMsgT)
                    }
                    adapter.notifyDataSetChanged()
                    recyclerView.post { // Call smooth scroll
                        recyclerView.smoothScrollToPosition(adapter.itemCount - 1)
                    }
                }
            }
    }

>Solution :

Instead of notifyDataSetChanged() (which basically says "redisplay everything because something somewhere has changed in some way") you should use one of the more specific notify methods, like notifyItemRemoved. That way the Adapter and RecylerView can be smarter about visually updating, like avoiding scrolling and adding animations.

If you’re talking about that smoothScrollToPosition call you’re making, that looks like it happens whenever your data updated – and if I had to guess, I’d assume you’re doing those updates in deleteForMe() and deleteForAll(). In which case, smooth scrolling to the last item is what you’ve told it to do. If you want to avoid that, and it’s really only something that should happen once during setup, just add a flag or something:

// some top level / ViewModel property
var firstScrollDone = false

...

// in your data snapshot listener
if (!firstScrollDone) {
    firstScrollDone = true
    // do the scroll
}

There’s probably a better way to do it in your specific situation, but you get the idea!

Leave a Reply Cancel reply