Home > Mobile >  How to get ParcelableExtra from RecyclerView
How to get ParcelableExtra from RecyclerView

Time:06-30

I had one learning project that require RecyclerView item to send his detailed data to another activity with getParcelableExtra, my problem is when I clicked the item, the data is null on detailsActivity then I check it with the log.d, and yes it's null. any solution ;

here's the code main activity

class MainMyRecyclerViewActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMyrecyclerviewMainBinding
    private lateinit var rvHeroes: RecyclerView
    private val list = ArrayList<Hero>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMyrecyclerviewMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        rvHeroes = binding.rvMymainRv
        rvHeroes.setHasFixedSize(true)
        list.addAll(listHeroes)
        showRecyclerList()
    }

    private fun showRecyclerList() {
        //on change orientation adapter behavior
        if (applicationContext.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            rvHeroes.layoutManager = GridLayoutManager(this, 2)
        } else {
            rvHeroes.layoutManager = LinearLayoutManager(this)
        }

        val listHeroAdapter = ListHeroAdapter(list)
        rvHeroes.adapter = listHeroAdapter

        listHeroAdapter.setOnItemClickCallBack(object : OnItemClickCallback {
            override fun onItemClicked(data: Hero) {
                showSelectedHero(data)
                sendIntent(data)
            }
        })
    }

    private fun sendIntent(dataHero: Hero) {
        val intent = Intent(this@MainMyRecyclerViewActivity, DetailsActivity::class.java)
        intent.putExtra("DATA", dataHero.photo)
        intent.putExtra("DATA", dataHero.name)
        intent.putExtra("DATA", dataHero.description)

        startActivity(intent)
    }

    private val listHeroes: ArrayList<Hero>
        get() {
            val dataName = resources.getStringArray(R.array.data_name)
            val description = resources.getStringArray(R.array.data_description)
            val dataPhoto = resources.obtainTypedArray(R.array.data_photo)
            val listHero = ArrayList<Hero>()
            for (i in dataName.indices) {
                val hero = Hero(dataName[i], description[i], dataPhoto.getResourceId(i, -1))
                listHero.add(hero)
            }
            return listHero
        }

    private fun showSelectedHero(hero: Hero) {
        Toast.makeText(this, "you selected ${hero.name}", Toast.LENGTH_SHORT).show()
    }
}

here's the adapter

class ListHeroAdapter(private val listHero: ArrayList<Hero>) :
    RecyclerView.Adapter<ListViewHolder>() {

    private lateinit var onItemClickCallback: OnItemClickCallback

    fun setOnItemClickCallBack(onItemClickCallback: OnItemClickCallback) {
        this.onItemClickCallback = onItemClickCallback
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
        val view: View =
            LayoutInflater.from(parent.context).inflate(R.layout.item_myrecyclerview, parent, false)
        return ListViewHolder(view)
    }

    override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
        val (name, description, photo) = listHero[position]
        holder.imgPhoto.setImageResource(photo)
        holder.tvName.text = name
        holder.tvDescription.text = description
        holder.itemView.setOnClickListener {
            onItemClickCallback.onItemClicked(listHero[holder.adapterPosition])
        }
    }

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

this the target activity

class DetailsActivity : AppCompatActivity() {

    private lateinit var binding: ActivityDetailsBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityDetailsBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val data = intent.getParcelableExtra<Hero>("DATA")

        binding.tvDetailsNameHero.text = data?.name
        binding.tvDetailsDeskription.text = data?.description
        data?.photo?.let { binding.ivDetails.setImageResource(it) }

        Log.d("details data", data?.name.toString())

    }
}

here's is data class I've using

@kotlinx.parcelize.Parcelize
data class Hero(
    var name: String,
    var description: String,
    var photo: Int
) : Parcelable

the interface

interface OnItemClickCallback {

    fun onItemClicked(data: Hero)
}

any suggestion will be good for me, thank you.

CodePudding user response:

The problem is here

intent.putExtra("DATA", dataHero.photo)
intent.putExtra("DATA", dataHero.name)
intent.putExtra("DATA", dataHero.description)

You are sending single item in your intent as a String with the same key name

The first solution would be you need to send your class object And the second solution should send all extra parameters with different key names

the First solution You need to pass your model class object in intent

intent.putExtra("DATA",dataHero);

instead of this

private fun sendIntent(dataHero: Hero) {
        val intent = Intent(this@MainMyRecyclerViewActivity, DetailsActivity::class.java)
        intent.putExtra("DATA", dataHero.photo)
        intent.putExtra("DATA", dataHero.name)
        intent.putExtra("DATA", dataHero.description)

        startActivity(intent)
    }

The Second solution send data like this with different key names

intent.putExtra("PHOTO", dataHero.photo)
intent.putExtra("NAME", dataHero.name)
intent.putExtra("DESCRIPTION", dataHero.description)

and receive data like this in your details activity

val photo = intent.getStringExtra("PHOTO");
val name = intent.getStringExtra("NAME");
val description = intent.getStringExtra("DESCRIPTION");

CodePudding user response:

I think you're problem is here.

private fun sendIntent(dataHero: Hero) {
    val intent = Intent(this@MainMyRecyclerViewActivity, DetailsActivity::class.java)
    intent.putExtra("DATA", dataHero.photo)
    intent.putExtra("DATA", dataHero.name)
    intent.putExtra("DATA", dataHero.description)

    startActivity(intent)
}

What you're doing here is putting the value of dataHero.photo into the Bundle with the key DATA. Then you're overwriting that with dataHero.name, and then again with dataHero.description.

You can see from the image below that there are a lot of overloaded methods that use the same name but assign a different type.

enter image description here

So you are able to assign almost any value to a particular key (DATA), and the reason the call to retrieve the Parcelable is null, is because the value of DATA in the end is not a Parcelable implementation. The last assignment was of type String, which does not implement the Parcelable interface.

enter image description here

As mentioned by @AskNilesh use intent.putExtra("DATA", dataHero) instead.

CodePudding user response:

Change sendIntent method to this:

private fun sendIntent(dataHero: Hero) {
    val intent = Intent(this@MainMyRecyclerViewActivity, DetailsActivity::class.java)
    intent.putExtra("DATA", dataHero)
    startActivity(intent)
}
  • Related