Hyejung
Hyejung

Reputation: 1272

Kotlin: java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.recyclerview.widget.RecyclerView.setAdapter

I tried to make recyclerView on Fragment with data from Firestore.

I've made viewHolder and Adapter.

And all seems fine, but I keep facing below issue:

java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.recyclerview.widget.RecyclerView.setAdapter(androidx.recyclerview.widget.RecyclerView$Adapter)' on a null object reference

I know many questions are already asked, but I can't solve this problem with their solution.

Please help me which part cause problem in my code.

Data Class

data class DiaryCard(var name: String, var stepCount: String, var mood: Long? =2131230873, var diary: String? = "" )

Fragement:

class AllDiaryFragment : Fragment() {
private val db = Firebase.firestore
private val userDB = db.collection("users")
private val diaryDB = db.collection("diary")

private val userId = Firebase.auth.currentUser?.uid

private var _binding: FragmentAllDiaryBinding? = null
private val binding get() = _binding!!

private var username: String = "안녕"
private var userStepCount: String = "342"

private var items: ArrayList<DiaryCard> = ArrayList()


override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    _binding = FragmentAllDiaryBinding.inflate(inflater, container, false)
    val view = binding.root

    userDB
        .document("$userId")
        .get()
        .addOnSuccessListener { document ->
            username = document.data?.getValue("name").toString()
            userStepCount = document.data?.getValue("todayStepCount").toString()
        }


    diaryDB
        .get()
        .addOnSuccessListener { documents ->
            for (document in documents) {
                items.add(
                    DiaryCard(
                        username,
                        userStepCount,
                        (document.data?.getValue("todayMood") as Map<*,*>)["image"] as Long,
                        document.data?.getValue("todayDiary").toString(),
                    )
                )
            }

        }

    var adapter = DiaryCardAdapter(requireContext(), items)
    recyclerDiary.adapter = adapter
    recyclerDiary.layoutManager = LinearLayoutManager(
        context,
        RecyclerView.VERTICAL,
        false
    )

    return view
 }
}

Adapter

class DiaryCardAdapter(val context: Context, items: ArrayList<DiaryCard>) :
    RecyclerView.Adapter<DiaryCardAdapter.CardViewHolder>() {

    var items = items

    inner class CardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        var userNameView: TextView = itemView.findViewById<TextView>(R.id.userName)
        var userStepCountView: TextView = itemView.findViewById<TextView>(R.id.userStepCount)
        var userMoodView: ImageView = itemView.findViewById<ImageView>(R.id.userMood)
        var userDiaryView: TextView = itemView.findViewById<TextView>(R.id.userDiary)

        fun bind(position: Int) {
            userNameView.text = items[position].name
            userStepCountView.text = items[position].stepCount
            userMoodView.setImageResource((items[position].mood ?: 2131230873).toInt())
            userDiaryView.text = items[position].diary

        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
        val v = LayoutInflater.from(parent.context).inflate(R.layout.diary_card, parent, false)
        return CardViewHolder(v)
    }

    override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
        holder.bind(position)
    }

    override fun getItemCount(): Int = items.size
}

Thank you!

Upvotes: 1

Views: 292

Answers (2)

DHAVAL A.
DHAVAL A.

Reputation: 2321

It seems like you have mixed data binding with the kotlin synthetics.

Just avoid using the synthetics to prevent the null pointer exception.

Replace recyclerDiary with the binding.recyclerDiary.

Update:

 diaryDB
        .get()
        .addOnSuccessListener { documents ->
            for (document in documents) {
                items.add(
                    DiaryCard(
                        username,
                        userStepCount,
                        (document.data?.getValue("todayMood") as Map<*,*>)["image"] as Long,
                        document.data?.getValue("todayDiary").toString(),
                    )
                )
            }

        }

This function is asynchronous. That means addOnSuccessListener will be called after you set up the adapter with recyclerview.

So you have to notify the adapter about the change in items. You can do that by calling adapter.notifyDatasetChanged() in success listener. So final code will be look a like,

var adapter = DiaryCardAdapter(requireContext(), items)
diaryDB
    .get()
    .addOnSuccessListener { documents ->
        for (document in documents) {
            items.add(
                DiaryCard(
                    username,
                    userStepCount,
                    (document.data?.getValue("todayMood") as Map<*,*>)["image"] as Long,
                    document.data?.getValue("todayDiary").toString(),
                )
            )
        }
        adapter.notifyDatasetChanged()
    }

recyclerDiary.adapter = adapter

Upvotes: 3

Nguyễn Trung Hiếu
Nguyễn Trung Hiếu

Reputation: 2032

Add binding before recyclerDiary and try again!

binding.recyclerDiary.adapter = adapter
binding.recyclerDiary.layoutManager = LinearLayoutManager(
    context,
    RecyclerView.VERTICAL,
    false
)

Upvotes: 1

Related Questions