RecyclerViewAdapter in Android Kotlin - android

I have an array that save different sensors data (in non-activity class) and I want the RecyclerView to be updated based on data of that array. Is it possible to automatically change the presented data?
I used setOnClickListener but nothing updated. The RecyclerView just display the default data. Also, I used text view however the data is updated with each click not continuously.
Thanks for your help.
In fragment:
override fun onClick(v: View?) {
when (v?.id) {
R.id.buttonStart-> {
start(v)
}
R.id.buttonStop-> {
stop(v)
}
}
}
fun start (_v: View) {
listListSensors.adapter = SensorsRecyclerViewAdapter(model.listSensors(), mListener!!)
}
In the other class:
fun listSensors(): Sensors {
return currentSensor
}
In recycle view:
class SensorsRecyclerViewAdapter (items: Sensors, listener: ListSensorsFragment.OnListFragmentInteractionListener)
: RecyclerView.Adapter<SensorsRecyclerViewAdapter.SensorsViewHolder>() {
private var mValues: Sensors = items
private var mListener: ListSensorsFragment.OnListFragmentInteractionListener = listener
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SensorsViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.recyclerviewlistsensors_item, parent, false)
return SensorsViewHolder(itemView)
}
override fun onBindViewHolder(holder: SensorsViewHolder, position: Int) {
holder.mItem = mValues
holder.listSensorsLightView.text = mValues.getLight()
holder.listSensorsTemperatureView.text = mValues.getTemperature()
holder.listSensorsGyroscopeView.text = mValues.getGyroscope()
holder.listSensorsAccelerometerView.text = mValues.getAccelerometer()
holder.listSensorsGravityView.text = mValues.getGravity()
holder.mView.setOnClickListener { mListener.onListFragmentInteraction(holder.mItem) }
}

in your Adapter
var itemClickListener: ((position: Int, name: String) -> Unit)? = null
fun setData(data:List<AnyList>){
this.list.clear()
this.list.addAll(data)
notifyDataSetChanged()
}
//bindviewholder
itemClickListner.invoke(1,"anyvalue")
// in fragment
adapter.itemClickListener = {
position, name ->
Toast.makeText(requireContext(),"position is $position name is $name ",Toast.LENGTH_SHORT).show()
}
// call setData() any place in fragment

Related

RecyclerView items changes randomly on clicking an item

I have a RecyclerView list, I want the color of the clicked item to be changed. But whenever I am clicking an item the RecyclerView items change randomly.
I have also taken a boolean in my data class to keep track of item selection.
data class
data class OrderAvailableDaysResponseItem(
val actual_date: String,
val day_name: String,
val date_name: String,
val day_num: Int,
// For item selection
var isDateSelected: Boolean)
Inside my fragment, checking if the clicked item matches the list item and updating the isDateSelected to true, then calling notifydatasetchanged.
private var availableDaysList: OrderAvailableDaysResponse = OrderAvailableDaysResponse()
orderAvailableDaysAdapter.setDateClickListener {
for(resItem in availableDaysList){
resItem.isDateSelected = resItem.date_name == it.date_name
}
orderAvailableDaysAdapter.notifyDataSetChanged()
}
Adapter clicklistener
var onDateClickListener: ((OrderAvailableDaysResponseItem) -> Unit)? = null
fun setDateClickListener(listener: (OrderAvailableDaysResponseItem) -> Unit){
onDateClickListener = listener
}
inner class AvailableDaysViewHolder(binding: ItemAvailableDaysBinding) : RecyclerView.ViewHolder(binding.root) {
fun setBindings(itemRes: OrderAvailableDaysResponseItem){
binding.resItem = itemRes
binding.executePendingBindings()
binding.clRoot.setOnClickListener {
onDateClickListener?.let {
it(itemRes)
}
}
}
}
Please refer to the attachment for a better understanding of the situation
As you can see on clicking on an item the items are changing randomly. Please help me. Am I missing something?
Edit 1:
Complete adapter code
class OrderAvailableDaysAdapter(var orderAvailableDaysResponseList: OrderAvailableDaysResponse) : RecyclerView.Adapter<OrderAvailableDaysAdapter.AvailableDaysViewHolder>() {
private lateinit var binding: ItemAvailableDaysBinding
var onDateClickListener: ((OrderAvailableDaysResponseItem) -> Unit)? = null
fun setDateClickListener(listener: (OrderAvailableDaysResponseItem) -> Unit){
onDateClickListener = listener
}
inner class AvailableDaysViewHolder(binding: ItemAvailableDaysBinding) : RecyclerView.ViewHolder(binding.root) {
fun setBindings(itemRes: OrderAvailableDaysResponseItem){
binding.resItem = itemRes
binding.executePendingBindings()
binding.clRoot.setOnClickListener {
onDateClickListener?.let {
it(itemRes)
}
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AvailableDaysViewHolder {
binding = ItemAvailableDaysBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return AvailableDaysViewHolder(binding)
}
override fun onBindViewHolder(holder: AvailableDaysViewHolder, position: Int) {
holder.setBindings(orderAvailableDaysResponseList[position])
}
override fun getItemCount(): Int {
return orderAvailableDaysResponseList.size
}}
remove private lateinit var binding: ItemAvailableDaysBinding from adapter, don' keep it "globally", initialisation is made only once, in onCreateViewHolder
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AvailableDaysViewHolder {
ItemAvailableDaysBinding binding = ItemAvailableDaysBinding.inflate(
LayoutInflater.from(parent.context),parent,false)
return AvailableDaysViewHolder(binding)
}
same naming of this "global" object and in AvailableDaysViewHolder inner class may confuse it and setBindings may be called on lastly initialised (global kept) object rather that this passed in constructor

android checkbox recyclerview using kotlin

i wanna ask something about my code.
so, I wanna retrieve the data that i have selected in the checkbox recyclerview, and then put it into variabel arraylist. and then when i click button next, i can use variabel arraylist in main code and then i can move to next page. can you help me to add that code on my coding?
anyway i use kotlin, so i need code kotlin
here my main coding
private fun getData() {
mDatabaseReference.addValueEventListener(object : ValueEventListener {
override fun onCancelled(databaseError: DatabaseError) {
Toast.makeText(this#ChooseCategoryActivity, ""+databaseError.message, Toast.LENGTH_SHORT).show()
}
override fun onDataChange(dataSnapshot: DataSnapshot) {
dataCategory.clear()
for(getdataSnapshot in dataSnapshot.getChildren()) {
Log.i("dataKey", getdataSnapshot.key.toString())
val kategori = getdataSnapshot.getValue(Kategori::class.java)
dataCategory.add(kategori!!)
}
rv_user_kategori.adapter = UserCategoryAdapter(dataCategory) {
}
}
})
here my adapter recyclerview
class UserCategoryAdapter(private var data: List<Kategori>, private var listener: (Kategori) -> Unit) : RecyclerView.Adapter<UserCategoryAdapter.ViewHolder>() {
lateinit var ContextAdapter : Context
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
ContextAdapter = parent.context
val inflatedView = layoutInflater.inflate(R.layout.layout_row_kategori, parent, false)
return ViewHolder(inflatedView)
}
override fun getItemCount(): Int = data.size
override fun onBindViewHolder(holder: UserCategoryAdapter.ViewHolder, position: Int) {
holder.bindItem(data[position], listener,ContextAdapter, position)
}
class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
private val namaKategori:TextView = v.findViewById(R.id.txt_row_kategori)
var checkbox:CheckBox = v.findViewById(R.id.txt_row_kategori)
fun bindItem(data: Kategori, listener: (Kategori) -> Unit, context : Context, position : Int) {
namaKategori.text = data.nama
itemView.setOnClickListener {
listener(data)
}
}
}
}
please help me to find that solution.
Your listener currently return only the Kategori. Make it return also the position of the item on the list and/r the status of the checkbox. You can get the state of the checkbox with Checkbox.isChecked.
To use a listener you need to add invoke, so the flow should be :
Your adapter declaration
class UserCategoryAdapter(private var data: List<Kategori>, private var listener: (Kategori) -> Unit) : RecyclerView.Adapter<UserCategoryAdapter.ViewHolder>()
Then in your bind you send the listener via constructor as well
holder.bindItem(data[position], listener,ContextAdapter, position)
Once you are in the ViewHolder and you want to detect the state change listener you need to call it like
itemView.setOnClickListener { listener.invoke(data) }

Recyclerview click event not working with databinding and livedata

I'm new in Kotlin, I'm using databinding library to set items in recyclerview which I'm getting from viewmodel and calling clicklistener inside activity class. I have defined an interface in my adapter class and implemented in activity but nothing happens when I click on the recyclerview item and also not getting any error. I'm unable to figure out the mistake
My adapter looks like this
class NewsAdapter (private var listener: OnNewsClickListener) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val mCategoryList = ArrayList<Newslist>()
fun setAppList(categoryModel: ArrayList<Newslist>) {
mCategoryList.addAll(categoryModel)
//notifyItemRangeInserted(0, categoryModel.size)
notifyDataSetChanged()
}
override fun getItemCount(): Int {
Log.d("LIST_SIZE","" + mCategoryList.size)
return mCategoryList.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val appInfo = mCategoryList[position]
(holder as NewsAdapter.RecyclerHolderCatIcon).bind(appInfo, listener)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val applicationBinding = ItemNewsBinding.inflate(layoutInflater, parent, false)
return RecyclerHolderCatIcon(applicationBinding)
}
interface OnNewsClickListener {
fun onNewsClick(position: Int)
}
inner class RecyclerHolderCatIcon(private var applicationBinding:ItemNewsBinding) : RecyclerView.ViewHolder(applicationBinding.root) {
fun bind(appInfo:Newslist, listener: OnNewsClickListener?) {
applicationBinding.newsItem = appInfo
}
}
}
Because you have not set listener for for item layout.
You don't need to define OnNewsClickListener interface.
inner class RecyclerHolderCatIcon(private var applicationBinding:ItemNewsBinding) : RecyclerView.ViewHolder(applicationBinding.root) {
fun bind(appInfo:Newslist) {
applicationBinding.root.setOnClickListener{ /*handle click here*/ }
}
}
You need to set the click listener in bind like this.
Here is a sample code, you will need to modify it
fun bind(appInfo:Newslist, listener: OnNewsClickListener?) {
applicationBinding.newsItem = appInfo
itemView.setOnClickListener( view -> {
if (listener != null) {
listener.onNewsClick();
}
}
}

(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?) {
...
}

Can't assign new value to a simple Singleton using an onClickeListener for a RecyclerView

I am trying to build a Slack-like chat app following a tutorial in a course I am taking online.
In the tutorial the instructor is using a ListView and the OnItemClickListener method, but I am trying to do it with recycler view, and I am stuck with the onClickListener in the adapter.
I have tried to find answers in other questions but couldn't find one that solves my problem. The closest ones were this and this
My two problems are:
1. The app's main activity has on the top of the screen a title that states what channel is currently active. I have created a singleton that holds the "current channel" and the title's text is being pulled from that singleton.
I am having a hard time changing the value of that singleton on click.
The main activity also has all the channels in a list view in a drawer.
I am trying to close the drawer when a channel is clicked but that isn't happening either.
This is my current adapter:
class ChannelsAdapter(val context: Context, val channels: ArrayList<Channel>) :
RecyclerView.Adapter<ChannelsAdapter.Holder>() {
inner class Holder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val singleChannel = itemView.findViewById<TextView>(R.id.single_channel)
val mainLayout = LayoutInflater.from(context).inflate(R.layout.activity_main, null)
fun bindText(textVar: String, context: Context) {
singleChannel.text = textVar
}
}
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.bindText(channels[position].toString(), context)
holder.itemView.setOnClickListener {
ChannelName.activeChannel = channels[position]
holder.mainLayout.drawer_layout.closeDrawer(GravityCompat.START)
}
}
override fun getItemCount(): Int {
return channels.count()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelsAdapter.Holder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.channel_list_layout, parent, false)
return Holder(view)
}
}
This is the singleton
object ChannelName {
var activeChannel : Channel? = null
}
You can rewrite the setter for activeChanell variable and call a listener that has been added before to notify your Activity:
object ChannelName {
private val listeners = ArrayList<(Channel?) -> Unit>()
fun addChannelNameChangedListener(listener: (Channel?) -> Unit) {
listeners.add(listener)
}
fun removeChannelNameChangedListener(listener: (Channel?) -> Unit) {
listeners.remove(listener)
}
var activeChannel: Channel? = null
set(value) {
field = value
listeners.forEach { it.invoke(value) }
}
}
And inside the Activity add a listener like this:
ChannelName.addChannelNameChangedListener {
// Do your operation
}
The alternative solution is to use Observable utils like LiveData, so you shouldn't worry about the Android life cycle any more:
object ChannelName {
val activeChannel: MutableLiveData<ChannelName> = MutableLiveData()
}
To change the value inside your adapter simply call:
ChannelName.activeChannel.value = channels[position]
And inside your activity Observe to the variable by calling:
ChannelName.activeChannel.observe(this, Observer {
// Do your operation
})
class ChannelsAdapter(val context: Context, val channels: ArrayList<Channel>) :
RecyclerView.Adapter<ChannelsAdapter.Holder>() {
private var itemClickListener: OnItemClickListener? = null
fun setItemClickListener(itemClickListener: OnItemClickListener) {
this.itemClickListener = itemClickListener
}
interface OnItemClickListener {
fun onItemClick(position: Int)
}
inner class Holder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
val singleChannel = itemView.findViewById<TextView>(R.id.single_channel)
val mainLayout = LayoutInflater.from(context).inflate(R.layout.activity_main, null)
fun bindText(textVar: String, context: Context) {
singleChannel.text = textVar
}
override fun onClick(v: View?) {
val position = adapterPosition
itemClickListener?.let {
if (position != RecyclerView.NO_POSITION) {
it.onItemClick(position)
}
}
}
}
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.bindText(channels[position].toString(), context)
}
override fun getItemCount(): Int {
return channels.count()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelsAdapter.Holder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.channel_list_layout, parent, false)
return Holder(view)
}
}
This way you can setItemClickListener to the adapter in your activity and get callback from your recyclerView.
You should not set listener in onBind() method since it will be called more than your items' count.

Categories

Resources