Reputation: 4953
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
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