Reputation: 131
I have a fragment which holds a RecyclerView. Through this RecyclerView, I want a click to any of the views to open another fragment (which holds a different recycler view). Each view of the RecyclerView holds a picture URL and some text. I want both of these to be passed on to the second fragment so that I can use it in a ImageView and TextView.
I've managed to open the second fragment with the RecyclerView adapter but I'm having difficulty passing on the data from the RecyclerView to my second fragment.
Here is my code:
RecyclerView Adapter
class ManuAdapter(private val manu: List<Manufacturers>) :
RecyclerView.Adapter<ManuAdapter.ViewHolder>() {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val mpos = manu[position]
Picasso.get().load(mpos.bgphoto).into(holder.image)
holder.manufacturer.text = mpos.manufacturer
holder.itemView.setOnClickListener {
val activity = it.context as AppCompatActivity
CarListFragment.newInstance(mpos.manufacturer)
Timber.e("The string is: ${mpos.manufacturer}")
activity.supportFragmentManager.beginTransaction()
.replace(R.id.frameLayout, CarListFragment())
.addToBackStack(null)
.commit()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.manu_row, parent, false)
return ViewHolder(view)
}
override fun getItemCount() = manu.size
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val image: ImageView = itemView.manu_photo
val manufacturer: TextView = itemView.manufacturer
}
}
The Fragment I am trying to send information to
class CarListFragment : Fragment() {
companion object {
private const val ARG_TEXT = "myFragment_text"
fun newInstance(text: String): CarListFragment {
val args = Bundle()
args.putString(ARG_TEXT, text)
val fragment = CarListFragment()
fragment.arguments = args
return fragment
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val root = inflater.inflate(R.layout.fragment_car_list, container, false)
//RecyclerView in the second fragment
val carsRepository = CarsRepository().getAllCars()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
root.recycler_view.apply {
layoutManager = LinearLayoutManager(activity)
adapter = CarsAdapter(it)
root.loadingicon.visibility = View.GONE
root.loadingtext.visibility = View.GONE
}
}, {
})
val index = arguments?.getString(ARG_TEXT,"")
Timber.e("index is: $index")
root?.manu_name?.text = index
return root
}
}
After clicking on the view from RecyclerView, the log statement in my Adapter class shows me the correct String is being placed into the newInstance constructor. However, in my second fragment, my log statement for index shows that it is null.
Upvotes: 0
Views: 3828
Reputation: 131
Okay I managed to figure out how to get my data to my second fragment. The solution I found was not too complicated so I think this would help someone just coming into coding/kotlin like I am. It might not be the most efficient way but it's good enough for me.
Basically, I added an interface and a listener to communicate information from a click on the recyclerview to fragment #1 that holds the recyclerview to fragment #2. The three pieces of data I communicated were the position of the list, and two strings (one of which is an image URL).
The two sources I used to develop the code is a video that walks through how to get data through a onClickListener from the RecyclerView to my fragment #1 (MainFragment) and a tutorial that showed how to communicate that data to fragment #2 (CarListFragment).
First, I created the interface: I put this in it's own file just to keep things a bit tidier
package com.myname.mypackage
interface Communicator {
fun passData(position: Int, name: String, image: String)
}
In my RecyclerView Adapter:
private val listener: Communicator
to the adapter constructor at the topView.OnClickListener
after the extends ":"override fun onClick(v: View?)
The steps for this were followed from the video I linked.
class ManuAdapter(
private val manu: List<Manufacturers>,
private val listener: Communicator
) :
RecyclerView.Adapter<ManuAdapter.ViewHolder>() {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val currentItem = manu[position]
Picasso.get().load(currentItem.bgphoto).into(holder.image)
holder.manu_name.text = currentItem.manufacturer
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.manu_row, parent, false)
return ViewHolder(view)
}
override fun getItemCount() = manu.size
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
val image: ImageView = itemView.manu_photo
val manu_name: TextView = itemView.manufacturer
init {
itemView.setOnClickListener (this)
}
override fun onClick(v: View?) {
val position = adapterPosition
val image = manu[adapterPosition].bgphoto
val name = manu[adapterPosition].manufacturer
if (position != RecyclerView.NO_POSITION) {
listener.passData(position,name, image)
}
}
}
}
In my fragment #1 (which holds the RecyclerView):
line adapter = ManuAdapter(it, this@MainFragment)
Everything up to the creation of the onItemClick function is covered in the video, from here I followed the tutorial the rest of the way.
class MainFragment : Fragment(R.layout.fragment_main), Communicator {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val root = inflater.inflate(R.layout.fragment_main, container, false)
val carsRepository = CarsRepository().getAllManufacturers()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
root.recycler_view_main.apply {
layoutManager = LinearLayoutManager(activity)
adapter = ManuAdapter(it, this@MainFragment)
root.loadingicon_main.visibility = View.GONE
root.loadingtext_main.visibility = View.GONE
}
}, {
})
return root
}
override fun passData(position: Int, name: String, image: String) {
val bundle = Bundle()
bundle.putInt("input_pos", position)
bundle.putString("input_name", name)
bundle.putString("input_image", image)
val transaction = this.parentFragmentManager.beginTransaction()
val frag2 = CarListFragment()
frag2.arguments = bundle
transaction.replace(R.id.frameLayout, frag2)
transaction.addToBackStack(null)
transaction.commit()
}
}
In Fragment #2
class CarListFragment : Fragment() {
var inputPos: Int? = null
var inputName: String = ""
var inputImage: String = ""
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val root = inflater.inflate(R.layout.fragment_car_list, container, false)
//code for my fragment in here
inputPos = arguments?.getInt("input_pos")
inputName = arguments?.getString("input_name").toString()
inputImage = arguments?.getString("input_image").toString()
Picasso.get().load(inputImage).into(root?.manu_pic)
root?.manu_name?.text = inputName
return root
}
}
manu_pic and manu_name are just the id's of the ImageView and TextView in the XML inflated by the fragment.
Hope someone finds this information useful.
Upvotes: 3