Reputation: 1
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)
<?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>
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)
}
}
}
}
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)
}
}
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
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