Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

notifyItemChanged() not working when called within setOnUtteranceProgressListener

Edit

I've created a demo project on Github showing the exact problem. Git Project.


I've written an expandable recyclerView in Kotlin Every row has a play button which uses TextToSpeech. The text of the play button should change to stop whilst its playing, then change back to play when it finishes.

When I call notifyItemChanged within onStart and onDone of setOnUtteranceProgressListener, onBindViewHolder is not called and the rows in the recyclerView will no longer expand and collapse correctly.

t1 = TextToSpeech(context, TextToSpeech.OnInitListener { status ->
    if (status != TextToSpeech.ERROR) {
        t1?.setOnUtteranceProgressListener(object : UtteranceProgressListener() {
            override fun onStart(utteranceId: String?) {
                recyclerView.adapter.notifyItemChanged(position)
            }

            override fun onStop(utteranceId: String?, interrupted: Boolean) {
                    super.onStop(utteranceId, interrupted)
                    onDone(utteranceId)
            }

            override fun onDone(utteranceId: String?) {
                val temp = position
                position = -1
                recyclerView.adapter.notifyItemChanged(temp)
            }

            override fun onError(utteranceId: String?) {}
//                    override fun onError(utteranceId: String?, errorCode: Int) {
//                        super.onError(utteranceId, errorCode)
//                    }
        })
    }
})

onBindViewHolder:

override fun onBindViewHolder(holder: RabbanaDuasViewHolder, position: Int){
    if (Values.playingPos == position) {
        holder.cmdPlay.text = context.getString(R.string.stop_icon)
    }
    else {
        holder.cmdPlay.text = context.getString(R.string.play_icon)
    }
}

How can I call notifyItemChanged(position) from within setOnUtteranceProgressListener or what callback can I use so that notifyItemChanged only executes when it's safe to execute?

like image 538
Dan Bray Avatar asked Mar 08 '19 10:03

Dan Bray


2 Answers

I tried to replicate your issue and I came to know that it is not working because methods of UtteranceProgressListener is not called on main thread and that's why onBindViewHolder method of the adapter is not called.

This worked for me and should work for you too:

Use runOnUiThread{} method to perform actions on RecyclerView like this:

t1.setOnUtteranceProgressListener(object : UtteranceProgressListener() {
    override fun onError(utteranceId: String?) {
    }

    override fun onStart(utteranceId: String?) {
        runOnUiThread {
            recyclerView.adapter?.notifyItemChanged(position)
        }
    }

    override fun onStop(utteranceId: String?, interrupted: Boolean) {
        super.onStop(utteranceId, interrupted)
        onDone(utteranceId)
    }

    override fun onDone(utteranceId: String?) {
        val temp = position
        position = -1
        runOnUiThread {
            recyclerView.adapter?.notifyItemChanged(temp)
        }
    }
}
like image 134
Birju Vachhani Avatar answered Nov 10 '22 08:11

Birju Vachhani


I solved the problem using runOnUiThread thanks to Birju Vachhani.

For a full working demo of not just the problem I had, but how to correctly expand and collapse rows in a RecyclerView (no onClick events in onBindViewHolder) see my Gitlab Demo Project.

like image 38
Dan Bray Avatar answered Nov 10 '22 09:11

Dan Bray