Kotlin : How to click on item in a Recyclerview Inside another Recyclerview - android

After seen all the similiar questions, I don't find a solution of my problem, which the description as follows:
In my android app, I use Kotlin as language. In my app, I have a lot of category and each category have a list of products. In my home activity, I create a vertical RecyclerView named "productListOfCategory". In the "productListOfCategory" adapter,a textView to display a category name and a recyclerView to display all related product list. The following code is the description of "ProductListOfProductTypeAdapter" adapter : (ProductType = category)
class ProductListOfProductTypeAdapter(private val context: Context, private val ProductTypeIDWithProduct: Map<String, Array<ProductData>>, private val productTypeDetail: Array<ProductTypeData>)
: RecyclerView.Adapter<ProductListOfProductTypeAdapter.ViewHolder>(),ProductOfProductTypeAdapter.OnItemClickListener{
override fun onItemClick(view: View, viewModel: ProductData) {
val intent = Intent(context,ProductDetail::class.java)
context.startActivity(intent)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_list_product_product_type, parent, false)
return ViewHolder(view)
}
override fun getItemCount() = ProductTypeIDWithProduct.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val itemType = ProductTypeIDWithProduct.keys.elementAt(position)
holder.productTypeName.text = getName(itemType)
val arrayProductOfProductType = ProductTypeIDWithProduct[itemType] as ArrayList<ProductData>
holder.productListOfProductType.apply {
holder.productListOfProductType.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
holder.productListOfProductType.adapter = ProductOfProductTypeAdapter(arrayProductOfProductType,context,this#ProductListOfProductTypeAdapter)
}
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val productTypeName: TextView = itemView.textViewProductTypeName
val productListOfProductType: RecyclerView = itemView.recyclerViewProductOfProductType
}
"ProductOfProductTypeAdapter" is the second adapter which the code as the following :
class ProductOfProductTypeAdapter(private val products: ArrayList<ProductData>, private val context: Context,private val mListener: OnItemClickListener)
: RecyclerView.Adapter<ProductOfProductTypeAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_product_featured, parent, false)
return ViewHolder(view)
}
override fun getItemCount() = products.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val productItem = products[position] as Map<String, Map<String, String>>
holder.productName.text = productItem["name"]?.get("En").toString()
holder.productWeight.text = productItem["weight"].toString().plus(context.resources.getString(R.string.ml))
holder.productPrice.text = "$".plus(productItem["price"].toString()).plus("/")
holder.productRating.text = productItem["reviewsValue"].toString()
holder.itemView.setOnClickListener {
mListener.onItemClick(holder.itemView, products[position])
notifyDataSetChanged()
}
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val productName: TextView = itemView.itemProdFeaturedName
val productPrice: TextView = itemView.itemProdFeaturedPrice
val productImage: ImageView = itemView.itemProdFeaturedImage
val productWeight: TextView = itemView.txtViewWeight
val productRating: TextView = itemView.itemProdFeaturedRate
}
interface OnItemClickListener {
fun onItemClick(view: View,viewModel: Map<String, Map<String, String>>)
}
My problem is How to click on product and dispaly the product detail activity.I try as the following but still not get what I want.

Shouldn't your OnItemClickListener be like the following?
interface OnItemClickListener {
fun onItemClick(view: View, product: ProductData)
}
And you need to change the way you start your ProductDetail activity, and put your product ID or something to identify the product selected in the extra data of the intent. For example:
val intent = Intent(context,ProductDetail::class.java)
intent.putExtra("PRODUCT_ID", product.id)
context.startActivity(intent)

Related

how to make the items of my Firebase Recycler adapter onClick with kotlin android

with the help of FirestoreUI I established a recyclerview that contains items from my cloud firestore database in it . And now I would like to be able to trigger an event* after clicking on an item of this list ( *the event is basically a new activity where I can find more details about the item) I am using Kotlin and android studio .
this is the activity where I put all the code I needed :
class UserViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView)
class MeetingActivity : AppCompatActivity() {
val db = Firebase.firestore
private val newMeetingActivityRequestCode = 1
private lateinit var rvMeetings : RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_meeting)
rvMeetings = findViewById(R.id.rvMeetings)
val query : CollectionReference = db.collection("Meetings")
val options = FirestoreRecyclerOptions.Builder<Meeting>().setQuery(query , Meeting::class.java)
.setLifecycleOwner(this).build()
val adapter = object: FirestoreRecyclerAdapter<Meeting, UserViewHolder>(options){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.meeting_raw_layout, parent, false)
return UserViewHolder(view)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int, model: Meeting) {
val tvName : TextView = holder.itemView.findViewById(R.id.tv1)
val tvEmojis : TextView = holder.itemView.findViewById(R.id.tv2)
tvName.text = model.name
tvEmojis.text = model.time
}
}
rvMeetings.adapter = adapter
rvMeetings.layoutManager = LinearLayoutManager(this)
val fab: View = findViewById(R.id.fab)
fab.setOnClickListener {
fabOnClick()
}
}
private fun fabOnClick() {
val intent = Intent(this, AddMeetingActivity::class.java)
startActivityForResult(intent, newMeetingActivityRequestCode)
}
}
any help would be really appreciated and thanks !
setOnClickListener in your list item
override fun onBindViewHolder(holder: UserViewHolder, position: Int, model: Meeting) {
val tvName : TextView = holder.itemView.findViewById(R.id.tv1)
val tvEmojis : TextView = holder.itemView.findViewById(R.id.tv2)
tvName.text = model.name
tvEmojis.text = model.time
holder.itemView.setOnClickListener {
//do something
}

How to I access button inside a recycler view?

This is my RecyclerView Adaper
class RecyclerAdapter(private val recyclerList: List): RecyclerView.Adapter(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): mainRecyclerViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.quiz_item_recycler_view,
parent, false)
return mainRecyclerViewHolder(itemView)
}
override fun getItemCount() = recyclerList.size
override fun onBindViewHolder(holder: mainRecyclerViewHolder, position: Int) {
val currentItem = recyclerList[position]
holder.imageView.setImageResource(currentItem.imageResource)
holder.textView.text = currentItem.recyclerCardText
}
class mainRecyclerViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val imageView: ImageView = itemView.rec_image
val textView: TextView = itemView.text_view_1
}
}
And this is my data class
data class RecyclerItemMain(val imageResource: Int, val recyclerCardText: String, val button: Button)
Like this:
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
holder.yourButton.setOnClickListener { v ->
// Here your start your other activity or navigate to another fragment
}
}
Remember that if you intent to send the user to diferent activities depending on the button, you have to create a when expression what will send the user to an activity depending on another value in the recyclerItem, for example the text of the item, in your case currentItem.recyclerCardText
Here is your full adapter, re-organized:
class RecyclerAdapter(recyclerList: List<CategorySectionIcon>) :
RecyclerView.Adapter<YourAdapter.CustomViewHolder>() {
private var recyclerList: List<CategorySectionIcon>? = recyclerList
inner class CustomViewHolder(
//Get a reference to the Views in our layout//
val myView: View
) : RecyclerView.ViewHolder(myView) {
var textView: TextView = myView.findViewById(R.id.your_text)
var imageView: ImageView = myView.findViewById(R.id.your_image)
var yourButton: Button = myView.findViewById(R.id.iv_category_imagen_icon)
}
override//Construct a RecyclerView.ViewHolder//
fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val itemView =
layoutInflater.inflate(R.layout.quiz_item_recycler_view, parent, false)
return CustomViewHolder(itemView)
}
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
holder.textView.text = recyclerList!![position].recyclerCardText
holder.imageView.setImageResource(recyclerList!![position].image)
holder.yourButton.setOnClickListener { v ->
// Here your start your other activity or navigate to another fragment
}
}
//Calculate the item count for the RecylerView//
override fun getItemCount(): Int {
return recyclerList!!.size
}
}

How to send RecyclerView data to a BottomSheetDialog

I am developing a Recyclerview that pressing a button should open a BottomSheetDialog. I can open BottomSheet but I can't pass data to it. I tried using an interface earlier but it didn't work.
class MyAdapter(private val listaItens: List<Itens>, private val context: Context,
private val fragmentManager: FragmentManager) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val POST_TXT = 0
private val POST_IMG = 1
//some code ...
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when (viewType) {
POST_TXT -> {
val item = LayoutInflater.from(parent.context).inflate(R.layout.text, parent, false)
return ViewHolderTexto(item)
}
POST_IMG -> {
val item = LayoutInflater.from(parent.context).inflate(R.layout.image, parent, false)
return ViewHolderImage(item)
}
//some code ...
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = listaItens[position]
val usuarioLogado = UsuarioFirebase.getDadosUsuarioLogado()
when (holder.itemViewType) {
POST_TXT -> {
val viewHolderTexto = holder as ViewHolderTexto
viewHolderTexto.setIsRecyclable(false)
//some code ...
}
POST_IMG -> {
val viewHolderImage = holder as ViewHolderImage
viewHolderImage.setIsRecyclable(false)
//some code ...
holder.imageComentarioPostagemImage.setOnClickListener {
val comentariosBottomSheet = ComentariosBottomSheet()//open bottom sheet
comentariosBottomSheet.show(fragmentManager, comentariosBottomSheet.tag)
}
//some code ...
}
}
}
How to send RecyclerView data to a BottomSheetDialog?
Why don't you simply pass the data by constructor param of the Bottom sheet
val comentariosBottomSheet = ComentariosBottomSheet(data)//open bottom sheet
comentariosBottomSheet.show(fragmentManager, comentariosBottomSheet.tag)
How about his approach:
function/interface passed into the adapter
Note function is passed as last parameter
class MyAdapter( .... private val onClickCallBack:(item :Itens) ... {
....
}
Register clicklistener on the holder
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int){
val item = listaItens[position]
val usuarioLogado = UsuarioFirebase.getDadosUsuarioLogado()
....
holder.itemView.setOnClickListener{
onClickCallBack(item)
}
Add callback to the adapter where you want to show the bottom (sheet ex Activity)
As noted above the function was passed as last parameter to the Aconstructor so we can call it after like this:
val myAdapter:MyAdapter = MyAdapter(){
//ShowBottomScree
// 'it' is refenced as the passed data from adapter.
}

Having trouble getting position on the 2nd Array

I'm Using GSON to parse my JSON. I have an object that has 2 array object. I'm having a problem on getting the TransactionDetails position.
I have a recyclerView that populates the "Transactions" and then when I tap on that it should open the "TransactionDetails". But I'm having trouble on getting the position on TransactionDetails what array to read.
As you can see on the code I need to put a position on [0] because it only loads the 1st array on transaction details.
JSON Url
DetailedTransactionAdapter
class DetailedTransactionAdapter(val transactionFeed: TransactionFeed) : RecyclerView.Adapter<DetailCustomViewHolder>() {
override fun onBindViewHolder(holder: DetailCustomViewHolder, position: Int) {
val tr = transactionFeed.Transactions[1].TransactionDetails[position]
holder.view.txt_programType.text = transactionDetail.ProgramType
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DetailCustomViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val cellForRow = layoutInflater.inflate(R.layout.list_detailed_transaction, parent, false)
return DetailCustomViewHolder(cellForRow)
}
override fun getItemCount(): Int {
return transactionFeed.Transactions[0].TransactionDetails.count()
}
}
class DetailCustomViewHolder(val view: View): RecyclerView.ViewHolder(view) {
init{
}
}
class TransactionFeed(val Transactions: List<Transactions>)
class Transactions(val TransactionCode: String,
val ProgramID: Int,
val ProgramName: String,
val ProgramType: String,
val ProgramDescription: String,
val TransactionID: Int,
val UserID: String,
val TransactionAmount: Int,
val TransactionDate: String,
val TransactionDetails: List<TransactionDetails>)
class TransactionDetails(val Clamied: Boolean,
val NextToClaim: Boolean,
val ProgramDetailID: Int,
val TransactionDetailID: Int,
val ProgramDetails: String,
val ProgramType: String,
val TransactionDetailAmount: Int,
val TransactionDetailMonth: String)
TransactionsAdapter
lass TransactionsAdapter(val transactionFeed: TransactionFeed) : RecyclerView.Adapter<CustomViewHolder>() {
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
val tr = transactionFeed.Transactions.get(position)
holder.view.txt_transaction_id.text = tr.TransactionID.toString()
holder.view.txt_transaction_date.text = tr.TransactionDate
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val cellForRow = layoutInflater.inflate(R.layout.list_transaction, parent, false)
return CustomViewHolder(cellForRow)
}
override fun getItemCount(): Int {
return transactionFeed.Transactions.count()
}
}
class CustomViewHolder(val view: View):RecyclerView.ViewHolder(view) {
init{
view.setOnClickListener {
val intent = Intent(view.context, DetailedTransactionActivity::class.java)
view.context.startActivity(intent)
}
}
}
You'll have to set appropriate position in getItemCount() and onBindViewHolder() of DetailedTransactionAdapter. Here is how you can go about doing it:
First pass selected transaction position in TransactionsAdapter (when user clicks on any transaction) via intent to DetailedTransactionActivity.
class CustomViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
init {
view.setOnClickListener {
val intent = Intent(view.context, DetailedTransactionActivity::class.java)
intent.putExtra("selectedTransaction", adapterPosition)
view.context.startActivity(intent)
}
}
}
Then get that position in DetailedTransactionActivity and pass it to DetailedTransactionAdapter.
val transactionPos = intent.getIntExtra("selectedTransaction", -1)
detailedTransactionAdapter = DetailedTransactionAdapter(transactionFeed, transactionPos)
You'll have to change your DetailedTransactionAdapter to also have selectedTransaction as a field. This will help in dynamically populating adapter and binding appropriate data to view. Change adapter impl to this:
class DetailedTransactionAdapter(val transactionFeed: TransactionFeed, val selectedTransaction:Int) : RecyclerView.Adapter<DetailCustomViewHolder>() {
override fun onBindViewHolder(holder: DetailCustomViewHolder, position: Int) {
if(selectedTransaction == -1){
return
}
val tr = transactionFeed.Transactions[selectedTransaction].TransactionDetails[position]
holder.view.txt_programType.text = transactionDetail.ProgramType
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DetailCustomViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val cellForRow = layoutInflater.inflate(R.layout.list_detailed_transaction, parent, false)
return DetailCustomViewHolder(cellForRow)
}
override fun getItemCount(): Int {
return transactionFeed.Transactions[selectedTransaction].TransactionDetails.count()
}
}
This is where magic happens. You are returning appropriate transaction details count
return transactionFeed.Transactions[selectedTransaction].TransactionDetails.count()
and binding data.
val tr = transactionFeed.Transactions[selectedTransaction].TransactionDetails[position]
So when you have selected first transaction, this will return count of transaction details of transaction[0] which is first transaction. And when any other transaction is selected, this will return its transaction details and populate adapter with its data.

(Kotlin) Position of RecyclerView returns -1 when trying to click on an item

I'm new to Android development (and Kotlin).
I'm trying to implement a RecyclerView (which works fine) and when I click on a specific row it opens a new activity (Intent).
However, whenever I've press/click on one of the rows, I'm only able to get the value "-1" returned.
I've tried a number of different approaches (you should see the number of tabs in my browser).
This seems like it should be a fairly straightforward occurrence for something as common as a RecyclerView, but for whatever reason I'm unable to get it working.
Here is my RecyclerView Adapter file:
class PNHLePlayerAdapter (val players : ArrayList<PNHLePlayer>, val context: Context) : RecyclerView.Adapter<ViewHolder>() {
var onItemClick: ((Int)->Unit) = {}
// Gets the number of items in the list
override fun getItemCount(): Int {
return players.size
}
// Inflates the item views
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val itemView = LayoutInflater.from(context).inflate(
R.layout.pnhle_list_item,
parent,
false
)
val viewHolder = ViewHolder(itemView)
itemView.setOnClickListener {
onItemClick(viewHolder.adapterPosition)
}
return ViewHolder(itemView)
}
// Binds each item in the ArrayList to a view
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.tvPlayerName?.text = players[position].Name
holder.tvPlayerRank?.text = position.toString()
holder.tvPNHLe?.text = players[position].PNHLe.toString()
holder.tvTeam?.text = players[position].Team
holder.ivLeague?.setImageResource(leagueImageID)
}
}
class ViewHolder (view: View) : RecyclerView.ViewHolder(view) {
val linLayout = view.hor1LinearLayout
val ivTeam = view.teamImageView
val tvPlayerName = view.playerNameTextView
val tvPlayerRank = view.rankNumTextView
val tvPNHLe = view.pnhleTextView
val tvTeam = view.teamTextView
val ivLeague = view.leagueImageView
}
As you can see, there is a class property "onItemClick" which uses a lambda as the click callback.
I setOnClickListener in the onCreateViewHolder method after the view is inflated.
Next, in my Activity I add the list to my Adapter and set the call back.
However, every time I 'Toast' the position it is displayed as '-1'.
val adapter = PNHLePlayerAdapter(list, this)
adapter.onItemClick = { position ->
Toast.makeText(this, position.toString(),Toast.LENGTH_SHORT).show()
var intent = Intent(this, PlayerCardActivity::class.java)
//startActivity(intent)
}
rv_player_list.adapter = adapter
Perhaps I'm not thinking about this properly, but shouldn't the position represent the row number of the item out of the RecyclerView???
Ideally, I need to use the position so that I can obtain the correct item from the 'list' (ArrayList) so that I can pass information to my next Activity using the Intent
I found the issue.
Change this line in onCreateViewHolder:
return ViewHolder(itemView)
to this one:
return viewHolder
I would reorganize the adapter like this:
class PNHLePlayerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter<Adapter.ViewHolder>() {
interface AdapterListener {
fun onItemSelected(position: Int?)
}
var players: List<Player> = listOf()
set(value) {
field = value
this.notifyDataSetChanged()
}
var listener: AdapterListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_car_selector, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(position)
}
override fun getItemCount(): Int {
return brands.size
}
inner class ViewHolder(view: View): androidx.recyclerview.widget.RecyclerView.ViewHolder(view) {
private var position: Int? = null
private val baseView: LinearLayout? = view.findViewById(R.id.baseView) as LinearLayout?
...
init {
baseView?.setOnClickListener {
listener?.onManufacturerSelected(position)
}
}
fun bind(position: Int) {
this.position = position
...
}
}
}
And from your activity/fragment set the listener as adapter.listener = this, and implement the onItemSelected(position: Int?)
override fun onItemSelected(position: Int?) {
...
}

Categories

Resources