skip to Main Content

I am creating a book-themed application in android. Briefly within my application I have three sections where a user can enter books, specifically: Daleggere, Incorso, Letti (this is Italian but translated would be: to be read, in progress, read). Each of these sections is an android recyclerview with an associated Adapter, also this recyclerview is populated through a realtime Firebase database. Now, the problem is that I created an interface in the adapter to create events on the click of buttons that I have in the layout of each individual element of the recyclerview, but when I go to open the fragment the app sends this exception: kotlin.UninitializedPropertyAccessException: lateinit property bListener has not been initialized.
This is the Fragment class CatalogoDaLeggere:

class CatalogoDaLeggere : Fragment() {

    private lateinit var binding: FragmentCatalogoDaLeggereBinding
    private lateinit var recyclerView : RecyclerView
    private lateinit var adapter: MyAdapterDL
    private lateinit var listaLibri: ArrayList<LibriDaL>
    private lateinit var select: Spinner
    private val sezioni = arrayListOf("In corso","Letti")
    private var isRecyclerViewPopulated = false

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
            binding = DataBindingUtil.inflate<FragmentCatalogoDaLeggereBinding>(inflater,
            R.layout.fragment_catalogo_da_leggere,container,false)

        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        listaLibri = ArrayList()

        recyclerView = binding.listaLibriLeggere
        recyclerView.layoutManager = LinearLayoutManager(requireContext())
        recyclerView.setHasFixedSize(true)


        adapter = MyAdapterDL(listaLibri)
        recyclerView.adapter = adapter

        checkBookCatalogo()

        if(isRecyclerViewPopulated) {
            adapter.setOnCLickItemListener(object : MyAdapterDL.onItemClickListener {
                override fun onItemClick(position: Int) {

                }

                override fun moveBook(spinner: Spinner, send: ImageButton) {
                    select = spinner
                    val btn = send
                    val arrayAdapter = ArrayAdapter<String>(
                        requireContext(),
                        android.R.layout.simple_spinner_dropdown_item
                    )
                    select.adapter = arrayAdapter
                    arrayAdapter.addAll(sezioni)
                    select.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
                        override fun onItemSelected(
                            parent: AdapterView<*>?,
                            view: View?,
                            position: Int,
                            id: Long
                        ) {
                            val selectedItem = parent?.getItemAtPosition(position).toString()
                        }

                        override fun onNothingSelected(parent: AdapterView<*>?) {
                            Toast.makeText(
                                requireContext(),
                                "Seleziona una sezione!",
                                Toast.LENGTH_SHORT,
                            ).show()
                        }

                    }
                    btn.setOnClickListener {
                        if (select.visibility == View.INVISIBLE) {
                            select.visibility = View.VISIBLE
                        } else {
                            select.visibility = View.INVISIBLE
                        }
                    }
                }

            })

        }
    }
    private fun checkBookCatalogo() {
        if (FirebaseAuth.getInstance().currentUser != null) {
            val cUser = FirebaseAuth.getInstance().currentUser!!
            Log.d("TAG", "Sono :")
            val database =
                FirebaseDatabase.getInstance("https://booktique-87881-default-rtdb.europe-west1.firebasedatabase.app/")
            val usersRef = database.reference.child("Utenti")
            val childRef = usersRef.child(cUser.uid)
            val catalogoRef = childRef.child("Catalogo")
            val daLeggereRef = catalogoRef.child("DaLeggere")
            /*
            val lettiRef = catalogoRef.child("Letti")
            val inCorsoRef = catalogoRef.child("InCorso")

             */

            daLeggereRef.addValueEventListener(object : ValueEventListener {
                override fun onDataChange(snapshot: DataSnapshot) {
                    val daLeggereBooks = arrayListOf<LibriDaL>()
                    if (snapshot.exists()) {
                        for (bookSnapshot in snapshot.children) {
                            val libriDaL = bookSnapshot.getValue(LibriDaL::class.java)
                            Log.d("TAG", "VolumeDet : $libriDaL")
                            daLeggereBooks.add(libriDaL!!)

                        }
                    }

                    // Richiama la funzione per i libri "DaLeggere"

                    loadBooks(daLeggereBooks)

                }

                override fun onCancelled(error: DatabaseError) {
                    // Gestisci eventuali errori nella lettura dei dati
                    Log.e("TAG", "Errore nel recupero dei dati", error.toException())
                }
            })
        }
    }

    private fun loadBooks(books: List<LibriDaL>?){
        if (books != null) {
            listaLibri.addAll(books)
            Log.d("TAG","LIBRI: $listaLibri" )
            adapter = MyAdapterDL(listaLibri)
            Log.d("TAG","LIBRI:11: $listaLibri" )
            recyclerView.adapter = adapter
            isRecyclerViewPopulated = true
        }
    }

}

And this is the Adapter, MyAdapterDL:

class MyAdapterDL(private val listaLibri : ArrayList<LibriDaL>) :
    RecyclerView.Adapter<MyAdapterDL.MyViewHolder>() {

    private lateinit var bListener : onItemClickListener

    interface onItemClickListener{
        fun onItemClick(position: Int)
        fun moveBook(spinner: Spinner, send : ImageButton)
    }

    fun setOnCLickItemListener(listener: onItemClickListener){
        bListener = listener
    }

    class MyViewHolder(itemView : View, listener: onItemClickListener) : RecyclerView.ViewHolder(itemView){

        val cover : ImageButton = itemView.findViewById(R.id.coverDL)
        val titolo : TextView = itemView.findViewById(R.id.titoloDL)
        val autore : TextView = itemView.findViewById(R.id.autoreDL)

        init {

            itemView.setOnClickListener {
                listener.onItemClick(adapterPosition)
            }

            itemView.findViewById<ImageButton>(R.id.sendDL).setOnClickListener {
                listener.moveBook(itemView.findViewById(R.id.spinnerDL),itemView.findViewById(R.id.sendDL))
            }

        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val itemView = LayoutInflater.from(parent.context).inflate(R.layout.lista_libri_da_leggere,parent,false)
        return MyViewHolder(itemView,bListener)
    }

    override fun getItemCount(): Int {
        return listaLibri.size
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val currentItem = listaLibri[position]

        Glide.with(holder.itemView.context)
            .load(currentItem.copertina)
            .into(holder.cover)

        holder.titolo.text = abbreviaInfo(currentItem?.titolo ?: "",25)

        holder.autore.text = abbreviaInfo(currentItem?.autori ?: "",25)
    }

    fun abbreviaInfo(stringa: String, lunghezzaMassima: Int): String {
        return if (stringa.length <= lunghezzaMassima) {
            stringa
        } else {
            val sottostringa = stringa.take(lunghezzaMassima)
            "$sottostringa..."
        }
    }


}

I am sure that the database call returns the books correctly, as before adding the listener to the adapter the recyclerview opened correctly and showed the books.
I also tried with "fake" books i.e. created directly in the class and in this case the listener gives no problems and works.
What I thought is that since the call to the database is asynchronous, the books are loaded after the page is opened and therefore the recyclerview at the time of creation has no elements on which to then be able to apply the listener and that is why it crashes. In fact I tried running the code:

            adapter.setOnCLickItemListener(object : MyAdapterDL.onItemClickListener {

only after setting the adapter, but it doesn’t go.
I tried this with a flag private var isRecyclerViewPopulated = false and setting it true at the end of the function loadBooks but nothing.
The only thing I have done more than when it worked is the spinner, but I don’t think that is the problem, as commenting on it doesn’t work anyway. I don’t know if maybe something is wrong in the call to database, I am new to this area. Thank you all very much in advance!

2

Answers


  1. Chosen as BEST ANSWER

    I succeeded, the problem was that I was going to create a new adapter here:

     private fun loadBooks(books: List<LibriDaL>?){
            if (books != null) {
                listaLibri.addAll(books)
                Log.d("TAG","LIBRI: $listaLibri" )
                adapter = MyAdapterDL(listaLibri)
                Log.d("TAG","LIBRI:11: $listaLibri" )
                recyclerView.adapter = adapter
                isRecyclerViewPopulated = true
            }
        }
    

    That had not set the listener, however, and so changing that function to this one:

     private fun loadBooks(books: List<LibriDaL>?){
            if (books != null) {
                listaLibri.addAll(books)
                adapter.notifyDataSetChanged()
            }
        }
    

  2. Your issue is here:

    if(isRecyclerViewPopulated) {
    

    this part of code executes before loadBooks is ever called as checkBookCatalogo is asynchronous, so

    adapter.setOnCLickItemListener
    

    where you set the listener is never called.
    So you either need to remove the if condition check or set the listener after loadBooks is called

    Some other issues you need to fix.

    Remove listener parameter from MyViewHolder and update onBindViewHolder to:

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.bind(listaLibri[position])
    }
    

    and move the code to the bind function in the viewholder, move all code in viewholder inside this bind function also remove the init block, and finally mark the viewholder class as inner

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search