How do I initialize a lazy variable for a RecyclerView adapter - kotlin

I have an adapter that takes in a list of items derived from a data class that I need to use for settings the items in the recyclerView.
The adapter looks something like this:
class MyAdapter(private var adapterItems: MutableList<MyDataClass>): RecyclerView.Adapter<ViewHolder>(){
override fun OnCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder{
//Todo
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int): ViewHolder {
//Todo
}
override fun getItemCount(): Int {
//Todo
}
inner class ViewHolder(
itemView: View)
: RecyclerView.ViewHolder(itemView)
Next I have my data class as
data class MyDataClass(
val id: Int,
val title: String,
val subTitle: String,
}
I am using the following way to create a variable for my adapter in my activity along with other member parameters that I have not mentioned here.
class Activity #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int): RecyclerView(context, attrs, defStyle){
private val myAdapter: MyAdapter by lazy {
MyAdapter(mutableListOf()
}
init{
this.adapter = myAdapter
//Along with layout manager and inflating the recyclerView
}
}
I have a Fragment class that is responsible for creating the items to be supplied to the adapter and check the working of the recycler view.
Thus doing so, I am facing the following error and have no clue on resolving it:
Attempt to invoke virtual method 'boolean
androids.recyclerview.widget.RecyclerView$ViewHolder.shouldIgnore()'
on a null object reference
I have duplicated my code over here for privacy. Any help is much appreciated.

Related

How to open different acitivies when click any recyclerView Items in Kotlin? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 months ago.
Improve this question
I got crazy ı am not able find solution. Please help me. 2 days ı am looking for solution. I clearly watn that if I click any recylerView Item starting a new activity. Each item has to own its activity.How to implement codes?. Thanks in advance.
My adapter code:
class CustomAdapter(private val mList: List<ItemsViewModel>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
// create new views
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
// inflates the card_view_design view
// that is used to hold list item
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.card_view_design, parent, false)
return ViewHolder(view)
}
// binds the list items to a view
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val ItemsViewModel = mList[position]
// sets the image to the imageview from our itemHolder class
holder.imageView.setImageResource(ItemsViewModel.image)
// sets the text to the textview from our itemHolder class
holder.textView.text = ItemsViewModel.text
holder.itemView.setOnClickListener {
}
}
// return the number of the items in the list
override fun getItemCount(): Int {
return mList.size
}
// Holds the views for adding it to image and text
class ViewHolder(ItemView: View) : RecyclerView.ViewHolder(ItemView) {
val imageView: ImageView = itemView.findViewById(R.id.imageview)
val textView: TextView = itemView.findViewById(R.id.textView)
}
}
My recyclerview.kt :
class recyclerView : AppCompatActivity() {
private lateinit var binding: ActivityRecyclerViewBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityRecyclerViewBinding.inflate(layoutInflater)
setContentView(binding.root)
supportActionBar?.hide()
// getting the recyclerview by its id
val recyclerview = findViewById<RecyclerView>(R.id.recyclerview)
// this creates a vertical layout Manager
binding.recyclerview.layoutManager = LinearLayoutManager(this)
// ArrayList of class ItemsViewModel
val data = ArrayList<ItemsViewModel>()
data.add(ItemsViewModel(R.drawable.jumping_jack, "Jumping Jack"))
data.add(ItemsViewModel(R.drawable.jumping_aquats, "Jumping Squat"))
// This will pass the ArrayList to our Adapter
val adapter = CustomAdapter(data)
// Setting the Adapter with the recyclerview
recyclerview.adapter = adapter
}
}
If you put a list object to adapter like param, you can get position of item, and use this position to start activity you want.
something like this:
add listener in your adapter
class CustomAdapter(
private val mList: List<ItemsViewModel>,
val clickListener: RecycleViewOnClickListener
) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
...
}
in your bindViewHolder:
override fun onBindViewHolder(holder:
RecyclerView.ViewHolder,position: Int) {
holder.itemView.setOnClickListener {
clickListener.onItemClick(position)
}
}
and add simple interface:
interface RecycleViewOnClickListener {
fun onItemCLick(position: Int)
}
in your activity, get model from list with your position:
class recyclerView : AppCompatActivity(), RecycleViewOnClickListener {
override fun onItemClick(pos: Int) {
val model = yourList[pos]
//start new activity
}
}
then when you init adapter:
// This will pass the ArrayList to our Adapter
val adapter = CustomAdapter(data, this)
You can do this by using high order function it will be easy
In you adapter class (pass list and high order function as callback)
class YourAdapter(private val itemList: List<Model>, private val onItemClicked: (item) -> Unit) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
// your code
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
onItemClicked(itemList[position])
}
In your activity/Fragment where you are assigning adapter assign in this way.
whenever item is clicked it will land here, and you can start your activity, you will get item also from adapter on which user clicked
YourAdapter(list) { item ->
//start activity
}
Note: Always write some code with question, it will be easy for us to answer you and guide you what is wrong in your code.
Happy Coding!

Problems using VIewHolder fun and variable

I'm having trouble binding my ViewHolder, and I've got two warning that I believe are related. I am trying to use Hilt to create a clickable ViewHolder, so in my SessionAdapter I am using an inner class to bind my SessionViewHolder to my RecyclerView.
First, I am struggling to understand what to return for the inner class SessionViewHolder fun bind(session: Session) { ...}. Android Studio is telling me function "bind" is never used, but I thought I used it in my onBindViewHolder?
Secondly, in my override onBindViewHolder I don't understand how I should use val session?
#AndroidEntryPoint
class SessionFragment : Fragment() {
var adapter: SessionAdapter = SessionAdapter()
private val sessionAdapter = SessionListAdapter(this::onSessionClicked)
private fun onSessionClicked(session: Session): Session {
return(session)
}
class SessionAdapter {
fun setOnClickListener() {
return(addSessionToItinerary())
}
private fun addSessionToItinerary() {
return addSessionToItinerary()
}
}
class SessionListAdapter(
private val onSessionCLicked: (Session) -> Unit,
) : ListAdapter<Session, SessionListAdapter.SessionViewHolder>(SessionItemCallback) {
inner class SessionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind() {
val textView = itemView.findViewById<TextView>(0)
fun bind(session: Session) {
textView.text = session.title
itemView.setOnClickListener {
onSessionCLicked(session)
return#setOnClickListener
}
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SessionViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val itemView = layoutInflater.inflate(R.layout.fragment_session_list, parent, false)
return SessionViewHolder(itemView)
}
override fun onBindViewHolder(holder: SessionViewHolder, position: Int) {
val session = getItem(position)
holder.bind()
}
}
Thank you in advance for you help. I have gotten myself confused with the recurrence of bind and session throughout my adapter.
See here in this code, you have defined two bind functions, but one is nested inside the other so it is unusable:
fun bind() {
val textView = itemView.findViewById<TextView>(0)
fun bind(session: Session) {
textView.text = session.title
itemView.setOnClickListener {
onSessionCLicked(session)
return#setOnClickListener
}
}
}
The outer bind() function is the one you are calling, and it doesn't make sense to bind nothing. This function gets a reference to a TextView, and it creates a function that is never used.
Another problem is that you are passing 0 to findViewById. There is never going to be a view with an ID of 0. You need to pass R.id.whateverYourTextViewIsNamedInYourXml.
Side note, return#onClickListener is unnecessary. If a function doesn't return anything, putting a return statement on the last line doesn't do anything.
To make it work, you should replace the above code with something like this, but replace the name of the text view with whatever ID you assigned it in your XML:
fun bind(session: Session) {
val textView = itemView.findViewById<TextView>(R.id.myTextView)
textView.text = session.title
itemView.setOnClickListener {
onSessionCLicked(session)
}
}
and then pass the session to this function when you call it.
Side note, your SessionAdapter class doesn't make any sense at all, but you're not using it for anything anyway. I would delete that.

How can I access a variable located in an adapter class from main activity [duplicate]

This question already has answers here:
RecyclerView Item Click Listener the Right Way
(14 answers)
Closed 1 year ago.
I want to access a variable in an adapter class called item so that I could use it in my main activity. So far I've tried adding open before the adapter class and using object and companion object. How can I fix this problem?
class GrammarAdapter(private val context: Context, private val items: ArrayList<String>) :
RecyclerView.Adapter<GrammarAdapter.ViewHolder>() {
class ViewHolder(binding: GrammarItemRowBinding) : RecyclerView.ViewHolder(binding.root) {
val tvItem = binding.tvItemName
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
GrammarItemRowBinding.inflate(LayoutInflater.from(context), parent, false)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = items[position]
holder.tvItem.text = item
holder.itemView.setOnClickListener {
when (item) {
"Countable and uncountable nouns" -> {
val intent = Intent(holder.itemView.context, CountableUncountable::class.java)
intent.putExtra("clickedGrammarTopic", item)
holder.itemView.context.startActivity(intent)
}
"Singular and plural nouns" -> {
val intent = Intent(holder.itemView.context, Singular and plural nouns::class.java)
intent.putExtra("clickedGrammarTopic", item)
holder.itemView.context.startActivity(intent)
}
...
As long as items is not private you can do something like:
val item = (myRecycler.adapter as MyAdapterClass).items[position]
However thats not really the best practice and you may want to rethink how your managing your apps data perhaps you should look into ViewModels
You have to use the callback pattern via an interface
interface Callback {
fun onClickedRow(item: String)
}
In the adapter constructor
class GrammarAdapter(... , private val callback: Callback) : ...
And modify the interaction
holder.itemView.setOnClickListener {
callback.onClickedRow(item)
}
You have to implement the interface in the activity or fragment
clas MyFragment : Callback {
override fun onClickedRow(item: String) {
//do something here with your item
}
}
And instantiate your adapter with the callback as an argument
val adapter = GrammarAdapter(..., this#MyFragment) //depending on your scope it can be simple this
Now when the user clicks the method override fun onClickedRow(... is going to be triggered on the activity or fragment

I am trying to create a RecyclerView in Kotlin but having what seems a simple issue

So im trying to create a RecyclerView but im getting errors on "class RecyclerAdapter" and "override fun onBindViewHolder" Reference photo down below
On the first it states "Class 'RecyclerAdapter' is not abstract and does not implement abstract base class member public abstract fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int): Unit defined in androidx.recyclerview.widget.RecyclerView.Adapter"
The second error states "onBindViewHolder' overrides nothing"
im am following this tutorial https://www.youtube.com/watch?v=ai9rSGcDhyQ&ab_channel=CodePalace Here the snippet from the tutorial that Im having problems with
I copied the tutorial almost word for word but I have no idea why im still getting these erros and trust me I have tried researching here and elsewhere including android documentation for solutions but to no avail. Here is what I tried to try to fix it:When I added RecyclerView.Viewholder both errors disappear but then the variables become unresolved
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder.itemTitle.text=titles[position]
holder.itemDetail.text=details[position]
holder.itemPicture.setImageResource(images[position])
}
I would be very grateful if you can find a solution to this issue.
thank you
class RecyclerAdapter (private var titles:List<String>,private var details: List<String>,private var
images:List<Int>):
RecyclerView.Adapter<RecyclerView.ViewHolder>(){
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val itemTitle: TextView = itemView.findViewById(R.id.title)
val itemDetail:TextView=itemView.findViewById(R.id.description)
val itemPicture: ImageView =itemView.findViewById(R.id.iv_image)
init {
// Define click listener for the ViewHolder's View.
itemView.setOnClickListener{v:View->
val position:Int =adapterPosition
Toast.makeText(itemView.context," You clicked on item #
${position+1}",Toast.LENGTH_SHORT)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v =LayoutInflater.from(parent.context).inflate(R.layout.item_layout,parent,false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemTitle.text=titles[position]
holder.itemDetail.text=details[position]
holder.itemPicture.setImageResource(images[position])
}
override fun getItemCount(): Int {
return titles.size
}
}
RecyclerAdapter.ViewHolder, not RecyclerView.ViewHolder
class RecyclerAdapter (private var titles:List<String>,private var details: List<String>,private var
images:List<Int>):
RecyclerView.Adapter<RecyclerAdapter.ViewHolder>()

I've tried to make a RecyclerView with an Adapter but I cannot see the layout?

I've tried to build a recyclerview with an adapter class and cardview layout however even though the code runs I cannot see the list with cardview (I just get a blank recyclerview)?
SpeakerAdapter.kt
class SpeakerAdapter (val speakers: ArrayList<String>): RecyclerView.Adapter<SpeakerAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val speakerName: TextView = itemView.findViewById(R.id.name)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.speakerName.text = speakers[position]
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val View = LayoutInflater.from(parent.context).inflate(R.layout.speaker_list, parent, false)
return ViewHolder(View)
}
override fun getItemCount() = speakers.size
}
Speaker.kt
class Speaker : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
setContentView(R.layout.activity_speaker)
val speakers : ArrayList<String> = ArrayList()
for (i in 1..100){
speakers.add("Speaker 1 #$i")
recyclerView_speaker.layoutManager = LinearLayoutManager(this)
recyclerView_speaker.adapter = SpeakerAdapter(speakers)
}
}}
It's a bit hard to debug without the xml. But here goes nothing:
I think it'd be worth a shot modifying your view holder class. The reference for the text view is being set when you init the view holder and the layout may not be inflated yet. Try finding the reference when you bind the data to the view to avoid this issue.
Try this:
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(name: String?) {
itemView. name.text = name
}
}
and then modify your adapter to call this method
class SpeakerAdapter (val speakers: ArrayList<String>): RecyclerView.Adapter<SpeakerAdapter.ViewHolder>() {
...
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(speakers[position])
}
}
PS.: it'd be a good idea to call your view holder something else. ViewHolder is a name that is already used by the android SDK. Ex.: SpeakerViewholder