Lesley Tulienge
Lesley Tulienge

Reputation: 1

Kotlin-Attempt to invoke virtual method 'void androidx.recyclerview.widget.RecyclerView.setAdapter(androidx.recyclerview.widget.RecyclerView$Adapter)'

I Am trying to run the application but it crashes when i try to access the content of a bottom navigation bar which has a fragment in it and the fragement cointains a recyclerView.The adpater is null here is the error

java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.recyclerview.widget.RecyclerView.setAdapter(androidx.recyclerview.widget.RecyclerView$Adapter)' on a null object reference 
at com.example.accers.ChatFragment.recyclerView(ChatFragment.kt:67)
at com.example.accers.ChatFragment.onCreateView(ChatFragment.kt:41)

Fragment xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ChatFragment">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <LinearLayout
        android:id="@+id/ll_layout_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="#E4E4E4"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/et_message"
            android:inputType="textShortMessage"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:layout_weight=".5"
            android:background="@drawable/round_button"
            android:backgroundTint="@android:color/white"
            android:hint="Type a message..."
            android:padding="10dp"
            android:singleLine="true" />

        <Button
            android:id="@+id/btn_send"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="10dp"
            android:layout_weight="1"
            android:background="@drawable/round_button"
            android:backgroundTint="#26A69A"
            android:text="Send"
            android:textColor="@android:color/white" />

    </LinearLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_messages"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/ll_layout_bar"
        tools:itemCount="20"
        tools:listitem="@layout/message_item" />
<!--    <View-->
        android:layout_below="@+id/dark_divider"
<!--        android:layout_width="match_parent"-->
<!--        android:layout_height="10dp"-->
<!--        android:background="#42A5F5"-->
<!--        android:id="@+id/dark_divider"/>-->
    </RelativeLayout>
</FrameLayout>

Fragment Class

class ChatFragment : Fragment() {
    private val TAG = "ChatFragment"


    var messagesList = mutableListOf<Message>()

    private lateinit var adapter: MessagingAdapter
    private val botList = listOf("Cassandra", "Francesca", "Luigi", "Nico","Lesley","Hiyle","Roselind")

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        // Inflate the layout for this fragment
        var view = inflater.inflate(R.layout.fragment_chat, container, false)

       // var button: Button = view.findViewById(R.id.btn_send)

        recyclerView()

        clickEvents()

        val random = (0..3).random()
        customBotMessage("Hello! Today you're speaking with ${botList[random]}, how may I help?")

        return view

    }

    private fun clickEvents() {
        //Send a message
        btn_send.setOnClickListener {
            sendMessage()
        }

       // Scroll back to correct position when user clicks on text view
        et_message.setOnClickListener {
            GlobalScope.launch {
                delay(100)

                withContext(Dispatchers.Main) {
                    rv_messages.scrollToPosition(adapter.itemCount - 1)

                }
            }
        }
    }


    private fun recyclerView() {
        adapter = MessagingAdapter()
        rv_messages.adapter = adapter
        rv_messages.layoutManager = LinearLayoutManager(activity)
    }


    override fun onStart() {
        super.onStart()
        //In case there are messages, scroll to bottom when re-opening app
        GlobalScope.launch {
            delay(100)
            withContext(Dispatchers.Main) {
                rv_messages.scrollToPosition(adapter.itemCount - 1)
            }
        }
    }

    private fun sendMessage() {
        val message = et_message.text.toString()
        val timeStamp = Time.timeStamp()

        if (message.isNotEmpty()) {
            //Adds it to our local list
            messagesList.add(Message(message, SEND_ID, timeStamp))
            et_message.setText("")

            adapter.insertMessage(Message(message, SEND_ID, timeStamp))
            rv_messages.scrollToPosition(adapter.itemCount - 1)

            botResponse(message)
        }
    }

    private fun botResponse(message: String) {
        val timeStamp = Time.timeStamp()

        GlobalScope.launch {
            //Fake response delay
            delay(1000)

            withContext(Dispatchers.Main) {
                //Gets the response
                val response = BotResponse.basicResponses(message)

                //Adds it to our local list
                messagesList.add(Message(response, RECEIVE_ID, timeStamp))

                //Inserts our message into the adapter
                adapter.insertMessage(Message(response, RECEIVE_ID, timeStamp))

                //Scrolls us to the position of the latest message
                rv_messages.scrollToPosition(adapter.itemCount - 1)

                //Starts Google
                when (response) {
                    OPEN_GOOGLE -> {
                        val site = Intent(Intent.ACTION_VIEW)
                        site.data = Uri.parse("https://www.google.com/")
                        startActivity(site)
                    }
                    OPEN_SEARCH -> {
                        val site = Intent(Intent.ACTION_VIEW)
                        val searchTerm: String? = message.substringAfterLast("search")
                        site.data = Uri.parse("https://www.google.com/search?&q=$searchTerm")
                        startActivity(site)
                    }

                }
            }
        }
    }

    private fun customBotMessage(message: String) {
        GlobalScope.launch {
            delay(1000)
            withContext(Dispatchers.Main) {
                val timeStamp = Time.timeStamp()
                messagesList.add(Message(message, RECEIVE_ID, timeStamp))
                adapter.insertMessage(Message(message, RECEIVE_ID, timeStamp))

                rv_messages.scrollToPosition(adapter.itemCount - 1)
            }
        }
    }

}

My Adapter class

class MessagingAdapter: RecyclerView.Adapter<MessagingAdapter.MessageViewHolder>() {

    var messagesList = mutableListOf<Message>()

    inner class MessageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        init {
            itemView.setOnClickListener {

                //Remove message on the item clicked
                messagesList.removeAt(adapterPosition)
                notifyItemRemoved(adapterPosition)
            }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageViewHolder {
        return MessageViewHolder(
            LayoutInflater.from(parent.context).inflate(R.layout.message_item, parent, false)
        )
    }

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

    @SuppressLint("SetTextI18n")
    override fun onBindViewHolder(holder: MessageViewHolder, position: Int) {
        val currentMessage = messagesList[position]

        when (currentMessage.id) {
            SEND_ID -> {
                holder.itemView.tv_message.apply {
                    text = currentMessage.message
                    visibility = View.VISIBLE
                }
                holder.itemView.tv_bot_message.visibility = View.GONE
            }
            RECEIVE_ID -> {
                holder.itemView.tv_bot_message.apply {
                    text = currentMessage.message
                    visibility = View.VISIBLE
                }
                holder.itemView.tv_message.visibility = View.GONE
            }
        }
    }

    fun insertMessage(message: Message) {
        this.messagesList.add(message)
        notifyItemInserted(messagesList.size)
    }

}

MainActivity Class

Main Activity class has the bottom nav bar to replace the fragments

val navHostFragment = supportFragmentManager.findFragmentById(R.id.mainContainer) as NavHostFragment
navController = navHostFragment.navController
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavigationView)
setupWithNavController(bottomNavigationView, navController)

I Have tried simillar solutions but i can't still figure how to apply simillar asked question and errors. Thank you

Upvotes: 0

Views: 291

Answers (1)

Gowtham K K
Gowtham K K

Reputation: 3429

You are using kotlin synthetics. Internally it will work as getView().findViewById(R.id.rv_messages)

Since in oncreateView, you are trying to access view before even view is attached in the fragment layout tree.

getView() will always return null.

Several things you can do. You can pass view to the recycler view function and access like view.rv_messages.

It's better to handle like the below.

Else you can move all view related to onViewCreated(). In onCreateView() you will just inflate and return the view. So in onViewCreated() when it calls getView() , since view is already added in onCreateView it will return the correct view object.

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {
    // Inflate the layout for this fragment
    var view = inflater.inflate(R.layout.fragment_chat, container, false)
    return view

}

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

        recyclerView()

        clickEvents()

        val random = (0..3).random()
        customBotMessage("Hello! Today you're speaking with ${botList[random]}, how may I help?")

    }

Also synthetics have been deprecated, and currently, it is not recommended. will strongly recommend you to use view binding for binding the views. As ,earlier I had a weird issue with synthetics which I have covered here.

Refer here for more details about deprecation of kotlin synthetics.

Upvotes: 1

Related Questions