I am trying to get data from RecylerView adapter into the fragment, after clicking on that data. I have tried solving this question using interface. But my app is crashing after clicking on it.
Here is the code of the adapter:-
class SearchPlaceAdapter(
private var mContext: Context,
private var mPlaces: List<String>,
private var isFragment: Boolean = false,
): RecyclerView.Adapter<SearchPlaceAdapter.ViewHolder>(){
private val onPlaceClickListener: MainActivity.OnPlaceClickListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view =
LayoutInflater.from(mContext).inflate(R.layout.rv_search_place, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val place = mPlaces[position]
holder.place.text = place
holder.place.setOnClickListener {
onPlaceClickListener!!.onPlaceClick(place)
}
}
override fun getItemCount(): Int {
return mPlaces.size
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var place: TextView =itemView.findViewById(R.id.searchPlaceTV)
}
}
here is MainActivity code:-
interface OnPlaceClickListener {
fun onPlaceClick(place: String?)
}
here is my fragment code to get the data:-
override fun onPlaceClick(place: String?) {
newPlace.text = place
}
I am getting this as an error. The error is in the adapter:-
java.lang.NullPointerException
at com.ehie.recyclerview.adapter.SearchPlaceAdapter.onBindViewHolder$lambda-0(SearchPlaceAdapter.kt:31)
at com.ehi.recyclerview.adapter.SearchPlaceAdapter.$r8$lambda$KIVoR28fNIxsomM1sHTPNEhSuXQ(Unknown Source:0)
at com.ehie.recyclerview.adapter.SearchPlaceAdapter$$ExternalSyntheticLambda0.onClick(Unknown Source:4)
at android.view.View.performClick(View.java:7792)
at android.widget.TextView.performClick(TextView.java:16112)
at android.view.View.performClickInternal(View.java:7769)
at android.view.View.access$3800(View.java:910)
at android.view.View$PerformClick.run(View.java:30218)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8751)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
CodePudding user response:
The problem is that you are not initializing your onPlaceClickListener
anywhere in the adapter, that's why it is throwing NullPointerException
when you do onPlaceClickListener!!.onPlaceClick(place)
. You should pass it as an argument to the adapter.
class SearchPlaceAdapter(
private val onPlaceClickListener: OnPlaceClickListener,
private val mPlaces: List<String>,
private val isFragment: Boolean = false,
): RecyclerView.Adapter<SearchPlaceAdapter.ViewHolder>() {
While creating the adapter from the fragment, you can pass this
for the onPlaceClickListener
parameter (it will work because your fragment implements the OnPlaceClickListener
interface).
Also, you don't need to pass mContext
to the adapter. In onCreateViewHolder
you can use parent.context
in place of mContext
.
An alternate approach to sending data from adapter to fragment is to use a lambda function instead of an interface.
class SearchPlaceAdapter(
private val mPlaces: List<String>,
private val isFragment: Boolean = false,
private val onPlaceClick: (String?) -> Unit,
): RecyclerView.Adapter<SearchPlaceAdapter.ViewHolder>() {
...
holder.place.setOnClickListener {
onPlaceClick(place)
}
...
}
// And in your fragment
val adapter = SearchPlaceAdapter(mPlaces = <yourList>) { place ->
newPlace.text = place
}
This approach is much more concise than using an interface.
CodePudding user response:
I was going to say you forgot to set your onPlaceClickListener
property, which you did, put also you marked it as private
, making it impossible to set it.
Then you use !!.
to call it. !!
means "crash the app if I happen to put a null value in this property". You should (almost) never use !!
. In this case, it would make sense to use a null-safe ?.
call, which would silently do nothing if the onPlaceClickListener
is not set.
But you need to make onPlaceClickListener
public (remove the private
keyword in front of it) and set its value in the Activity or Fragment. Or alternatively, you could move it into your constructor to require it.
You should mark your interface as a fun interface
to make it easier to work with. It will allow you to define it using a lambda (way less boilerplate).
Also, in my opinion, it is preferable not to make your Fragment directly implement the listener. It is cleaner (better encapsulation) to define your listener as a separate class instance, for example:
// In fragment:
mySearchPlaceAdapter.onPlaceClickListener = MainActivity.OnPlaceClickListener { place ->
newPlace.text = place
}