Hossein Yousefpour
Hossein Yousefpour

Reputation: 4953

ViewHolder cannot be cast to OtherViewHolder

I have a problem with my Recyclerview's custom adapter. I'm trying to display two different views with different data source in one recycler view. It was working great before I make some changes and now it gets an exception.

It's working fine until notifyDataSetChanged() function! The crash will happen when the holder.txtDelete.onClick or holder.txtEdit.onClick function fires!

I searched a lot but can't find the solution. Thanks for helping

My Custom Adapter:

import android.app.Activity
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.os.Vibrator
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.cardview.widget.CardView
import androidx.recyclerview.widget.RecyclerView
import artemis.team.picandtext.*
import com.squareup.picasso.Picasso
import org.jetbrains.anko.alert
import org.jetbrains.anko.sdk27.coroutines.onClick
import java.util.*

class RecAdapter(
    context: Context,
    activity: Activity,
    grplist: MutableList<GrpsValues> = mutableListOf(),
    itemslist: MutableList<ItemsValues> = mutableListOf()
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    private var context: Context? = null
    private var activity: Activity? = null
    private var grpList = mutableListOf<GrpsValues>()
    private var itemsList = mutableListOf<ItemsValues>()
    private lateinit var tts: TextToSpeech
    private var voiceText = "Text not found!"
    private var voiceLocale = "en-US"
    private var mode = 0
    private var itemsSize = 0
    private var groupsSize = 0
    private val ITEM = 100
    private val GROUP = 200
    private var HOLDER = GROUP

    init {
        this.activity = activity
        this.context = context
        this.grpList = grplist
        this.itemsList = itemslist
        itemsSize = itemsList.size
        groupsSize = grpList.size
    }

    override fun getItemCount(): Int {
        return (itemsList.size + grpList.size)
    } // getItemCount

    override fun onBindViewHolder(recHolder: RecyclerView.ViewHolder, pos: Int) {

        Mode()

        if (mode == ITEM && HOLDER == ITEM) {

            val holder = recHolder as ItemsViewHolder

            if (itemsList.size > 0) {
                val position = pos - grpList.size

                Picasso.get()
                    .load(itemsList[position].url)
                    .error(R.drawable.empty)
                    .into(holder.image)

                holder.title.text = itemsList[position].title
                holder.txtID.text = itemsList[position].id
                holder.grpID.text = itemsList[position].grpID

                val grpID = holder.grpID.text.toString()

                lateinit var dialog: DialogInterface

                holder.layout.onClick {
                    (context!!.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator?)!!.vibrate(30)
                    dialog = context!!.alert {
                        val view = activity!!.layoutInflater.inflate(R.layout.dialog_item_details, null)
                        val prgDatabase = PrgDatabase(context)
                        val value = prgDatabase.ItemsFind(holder.txtID.text.toString())
                        view.findViewById<TextView>(R.id.TxtText).text = value.title
                        Picasso.get()
                            .load(value.url)
                            .error(R.drawable.empty)
                            .into(view.findViewById<ImageView>(R.id.imageView))

                        customView = view
                    }.show()
                }

                holder.txtDelete.onClick {
                    (context!!.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator?)!!.vibrate(30)
                    context!!.alert {
                        title = "ItemsDelete"
                        message = "ItemsDelete item from the list?"

                        positiveButton("Yup") {
                            val prgDatabase = PrgDatabase(context)
                            prgDatabase.ItemsDelete(holder.txtID.text.toString())
                            itemsList = PrgDatabase(context).ItemsGetAll(grpID)

                            [email protected]()

                            it.dismiss()
                        }

                        negativeButton("NO") {
                            it.dismiss()
                        }

                    }.show()
                }

                holder.txtEdit.onClick {
                    (context!!.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator?)!!.vibrate(30)

                    dialog = context!!.alert {
                        val view = activity!!.layoutInflater.inflate(R.layout.dialog_items, null)
                        view.findViewById<Button>(R.id.BTNsaveID).text = "Save changes"
                        val prgDatabase = PrgDatabase(context)

                        val adapter = ArrayAdapter<String>(
                            context!!,
                            android.R.layout.simple_spinner_item,
                            context!!.resources.getStringArray(
                                R.array.langueges
                            )
                        )
                        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
                        view.findViewById<Spinner>(R.id.SpLangsID).adapter = adapter

                        val value = prgDatabase.ItemsFind(holder.txtID.text.toString())
                        view.findViewById<EditText>(R.id.ETtitleID).setText(value.title)
                        view.findViewById<EditText>(R.id.ETurlID).setText(value.url)
                        view.findViewById<Spinner>(R.id.SpLangsID).setSelection(
                            when (value.local) {
                                "English" -> 0
                                "Russian" -> 1
                                "Turkish" -> 2
                                "French" -> 3
                                "German" -> 4
                                "Italian" -> 5
                                "Korean" -> 6
                                else -> 0
                            }
                        )
                        view.findViewById<Button>(R.id.BTNsaveID).onClick {
                            if (view.findViewById<EditText>(R.id.ETtitleID).text.isNotEmpty()
                                && view.findViewById<EditText>(R.id.ETurlID).text.isNotEmpty()
                            ) {
                                val value = ItemsValues(
                                    title = view.findViewById<EditText>(R.id.ETtitleID).text.toString(),
                                    url = view.findViewById<EditText>(R.id.ETurlID).text.toString(),
                                    id = holder.txtID.text.toString(),
                                    grpID = grpID,
                                    local = view.findViewById<Spinner>(R.id.SpLangsID).selectedItem.toString()
                                )
                                prgDatabase.ItemsUpdate(
                                    value
                                )
                                itemsList[position] = value
                                dialog.dismiss()


                                [email protected]()
                                [email protected](position)


                            }// if
                        } // btn on click
                        customView = view

                    }.show()
                } // on click
            }

        } // if Items
        else if (mode == GROUP && HOLDER == GROUP) {

            val holder = recHolder as GrpsViewHolder
            if (grpList.size > 0) {

                val position = pos

                holder.titleGrp.text = grpList[position].title

                holder.txtItemIDGrp.text = grpList[position].id
                holder.txtParentGrpIDGrp.text = grpList[position].parent

                holder.layoutGrp.onClick {
                    val intent = Intent(context, MainActivity::class.java)
                    intent.putExtra("GrpID", holder.txtItemIDGrp.text.toString())
                    intent.putExtra("GrpTitle", grpList[position].title)
                    activity!!.startActivity(intent)
                }

                holder.txtDeleteGrp.onClick {
                    (context!!.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator?)!!.vibrate(30)
                    context!!.alert {
                        title = "Delete"
                        message = """
                        Delete Group from the list?

                        * With delete this group, all it's words and pictures will be removed!
                        """.trimIndent()

                        positiveButton("Yup") {
                            val prgDatabase = PrgDatabase(context)
                            prgDatabase.GrpsDelete(holder.txtItemIDGrp.text.toString())

                            grpList = PrgDatabase(context).GrpsGetAll(grpList[position].parent)

                            [email protected](position)
                            [email protected]()

                            it.dismiss()
                        }

                        negativeButton("NO") {
                            it.dismiss()
                        }

                    }.show()
                }

                lateinit var dialog: DialogInterface
                holder.txtEditGrp.onClick {
                    (context!!.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator?)!!.vibrate(30)

                    dialog = context!!.alert {
                        val view = activity!!.layoutInflater.inflate(R.layout.dialog_grps, null)

                        view.findViewById<Button>(R.id.BTNsaveID).text = "Save changes"
                        val prgDatabase = PrgDatabase(context)
                        val value = prgDatabase.GrpsFind(holder.txtItemIDGrp.text.toString())
                        view.findViewById<EditText>(R.id.ETtitleID).setText(value.title)
                        view.findViewById<Button>(R.id.BTNsaveID).onClick {
                            if (view.findViewById<EditText>(R.id.ETtitleID).text.isNotEmpty()) {
                                val value = GrpsValues(
                                    title = view.findViewById<EditText>(R.id.ETtitleID).text.toString(),
                                    id = holder.txtItemIDGrp.text.toString(),
                                    parent = holder.txtParentGrpIDGrp.text.toString()
                                )
                                prgDatabase.GrpsUpdate(
                                    value
                                )
                                grpList[position] = value
                                dialog.dismiss()

                                [email protected]()
                                [email protected](position)

                            }// if
                        } // btn on click
                        customView = view
                    }.show()
                } // on click
            } // if
        } // GROUP

    } // onBindViewHolder

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        if (groupsSize > 0) {
            HOLDER = GROUP
            return GrpsViewHolder(
                LayoutInflater.from(parent.context).inflate(
                    R.layout.custom_rec_grps_layout,
                    parent,
                    false
                )
            )

        } else {
            HOLDER = ITEM
            return ItemsViewHolder(
                LayoutInflater.from(parent.context).inflate(
                    R.layout.custom_rec_items_layout,
                    parent,
                    false
                )
            )
        }
    }

    inner class ItemsViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val image: ImageView = view.findViewById(R.id.imageView)
        val title: TextView = view.findViewById(R.id.TxtText)
        val txtID: TextView = view.findViewById(R.id.TxtItemID)
        val grpID: TextView = view.findViewById(R.id.TxtGrpID)
        val txtDelete: TextView = view.findViewById(R.id.TxtDeleteID)
        val txtEdit: TextView = view.findViewById(R.id.TxtEditID)
        val layout: CardView = view.findViewById(R.id.ConstItemsID)
    }

    inner class GrpsViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val titleGrp: TextView = view.findViewById(R.id.GrpTxtText)
        val txtItemIDGrp: TextView = view.findViewById(R.id.GrpTxtItemID)
        val txtParentGrpIDGrp: TextView = view.findViewById(R.id.GrpTxtGrpID)
        val txtDeleteGrp: TextView = view.findViewById(R.id.GrpTxtDeleteID)
        val txtEditGrp: TextView = view.findViewById(R.id.GrpTxtEditID)
        val layoutGrp: CardView = view.findViewById(R.id.CnstGrpsID)

    }

    override fun getItemViewType(position: Int): Int {
        return position
    }


    fun Mode() {
        if (groupsSize > 0) {
            groupsSize--
            mode = GROUP
        } else if (itemsSize > 0) {
            itemsSize--
            mode = ITEM
        }
    }
}

and this is the logcat error:

   --------- beginning of crash
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: artemis.team.picandtext, PID: 18621
    java.lang.ClassCastException: GrpsViewHolder cannot be cast to RecAdapter$ItemsViewHolder
       enter code here at RecAdapter.onBindViewHolder(RecAdapter.kt:66)
        at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:6781)
        at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:6823)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:5752)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6019)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5858)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5854)
        at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2230)
        at androidx.recyclerview.widget.GridLayoutManager.layoutChunk(GridLayoutManager.java:557)
        at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1517)
        at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:612)
        at androidx.recyclerview.widget.GridLayoutManager.onLayoutChildren(GridLayoutManager.java:171)
        at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3924)
        at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3336)
        at android.view.View.measure(View.java:22071)
        at androidx.constraintlayout.widget.ConstraintLayout.internalMeasureChildren(ConstraintLayout.java:1227)
        at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(ConstraintLayout.java:1572)
        at android.view.View.measure(View.java:22071)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
        at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:143)
        at android.view.View.measure(View.java:22071)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1514)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:806)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:685)
        at android.view.View.measure(View.java:22071)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
        at android.view.View.measure(View.java:22071)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1514)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:806)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:685)
        at android.view.View.measure(View.java:22071)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
        at com.android.internal.policy.DecorView.onMeasure(DecorView.java:724)
        at android.view.View.measure(View.java:22071)
        at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2422)
        at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1504)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1761)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1392)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6752)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:911)
        at android.view.Choreographer.doCallbacks(Choreographer.java:723)
        at android.view.Choreographer.doFrame(Choreographer.java:658)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
        at android.os.Handler.handleCallback(Handler.java:790)
        at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime:     at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)


    --------- beginning of system

Upvotes: 3

Views: 3834

Answers (1)

Rafa
Rafa

Reputation: 3339

You can't cast one child of the ViewHolder type to another type of the ViewHolder class, because they're not the same type.

    ViewHolder
       /\
RecHolder ItemsViewHolder

A RecHolder is a ViewHolder and an ItemsViewHolder is a viewholder, but just because they share the same parent doesn't make a RecHolder an ItemsViewHolder so the cast can never succeed.

Check your logic for determining what ViewHolder your onCreateViewHolder returns because that's the ViewHolder type that gets past into your onBindViewHolder

If you want a way to double check, inside of your logic, before you cast each one do if (viewHolder is RecHolder) or if(viewHolder is ItemViewHolder) to verify that you're getting the one you think you're getting.

Upvotes: 3

Related Questions