Strange animation when removing item from RecyclerView - android

I have a recyclerView populated by the val microphones: MutableList<Microphone>. There's a button in the row layout to remove that item from the list, which I've managed to get functioning, but the animation is going wrong. It looks like when the recyclerView updates, it removes the last item in the list, then the correct item, and then the whole list below the removed item animates to the correct state.
Here's the relevant bits of the recyclerView adapter:
class RecyclerViewAdapter(
val microphones: MutableList<Microphone>
): RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val cellForRow = LayoutInflater.from(parent.context).inflate(R.layout.favorites_row, parent, false)
return ViewHolder(cellForRow)
}
override fun getItemCount(): Int {
return microphones.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val microphone = microphones[position]
holder.bind(microphone)
holder.favoritesButton.setOnClickListener {
microphones.remove(microphone)
notifyItemRemoved(position)
}
}
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val modelText = view.findViewById<TextView>(R.id.model_textView)
val brandText = view.findViewById<TextView>(R.id.brand_textView)
val favoritesButton = view.findViewById<ImageButton>(R.id.btnFavorite)
fun bind(
microphone: Microphone,
) {
modelText.text = microphone.model
brandText.text = microphone.brand
}
}
}
Here's a screen recording of the issue:
https://imgur.com/a/pJt3KwH

Related

View all button to button of recycler view

Initially load 3 items from list after click on view all button load remaining all data to same page in kotlin.
To show 3 items I added limit to show data in adapter class in getItemCount mathod. So now I am able to see only 3 items from list. But not able to do like ad all data on same page on click view all button.
Here is how I have solved the issue.
class MyAdapter(private val maxItems: Int = 3) : RecyclerView.Adapter<MyViewHolder>() {
private val items = mutableListOf<String>()
fun setData(data: List<String>) {
items.clear()
items.addAll(data)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return min(items.size, maxItems)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(items[position])
}
fun updateMaxItems(max: Int) {
maxItems = max
notifyDataSetChanged()
}
}
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val textView = itemView.findViewById<TextView>(R.id.item_text)
fun bind(text: String) {
textView.text = text
}
}
In your Activity/Fragment where you set up the RecyclerView, create a button "View All" and set its onClickListener
val viewAllButton = findViewById<Button>(R.id.viewAllButton)
viewAllButton.setOnClickListener {
//update the adapter's maxItems to show all items
adapter.maxItems = Int.MAX_VALUE
adapter.notifyDataSetChanged()
}

How to get the current loaded rows/items of a RecyclerView

I have a list of items in my RecyclerView and I need to get the currently loaded items (i.e. not the recycled or off screen items) at a time.
This is may adapter
class MyAdapter(list: List<String>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
private var items: List<String> = list
class MyViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val myText = view.findViewById<TextView>(R.id.my_text);
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val listItem: View = LayoutInflater.from(parent.context)
.inflate(R.layout.list_item, parent, false)
return MyViewHolder(listItem)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.myText.text = items[position]
}
override fun getItemCount() = items.size
}
And RecyclerView:
recyclerView.apply {
layoutManager = LinearLayoutManager(this#MainActivity)
hasFixedSize()
adapter = MyAdapter(listOf("a", "b", "c")) // List is bigger than that
}
How can I do that?
I'd a case that I needed to ensure how many RecyclerView items are currently loaded in order to control a performance issue; so that created this for someone else was looking for an answer.
You need to track the list of loaded & recycled items:
The items are loaded in onBindViewHolder() callback
The items are recycled in onViewRecycled() callback
So, we can create a local list that tracks the current loaded item positions, add new positions in onBindViewHolder() and remove recycled ones in onViewRecycled()
In the below code the currentLoadedPositions tracks the loaded positions, and you can create a method that returns it:
class MyAdapter(list: List<String>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
private var items: List<String> = list
// Tracking the currently loaded items in the RecyclerView
private val currentLoadedPositions: ArrayList<Int> = ArrayList()
fun getLoadedPositions(): ArrayList<Int> {
return currentLoadedPositions
}
class MyViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val myText = view.findViewById<TextView>(R.id.my_text)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val listItem: View = LayoutInflater.from(parent.context)
.inflate(R.layout.list_item, parent, false)
return MyViewHolder(listItem)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = items[position]
holder.myText.text = item
currentLoadedPositions.add(position)
Log.d("LOG_TAG", "onViewRecycled: $currentLoadedPositions")
}
override fun getItemCount() = items.size
override fun onViewRecycled(holder: MyViewHolder) {
currentLoadedPositions.remove(Integer.valueOf(holder.adapterPosition));
Log.d("LOG_TAG", "onViewRecycled: $currentLoadedPositions")
}
}

How to add onClickListener to the items of the recyclerView in Kotlin?

I am using recyclerView to show data from firebase database and I want to handle clicks,
Now the important part is that I want to know the number that was clicked in order to test google play in app billing before showing the next activity
I mean user should click item number one then pay to see information number 1 and so on
Any help, please ?
//my adapter
class MyAdapter(
private val arrayList: ArrayList<Long>
) :
RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view =
LayoutInflater.from(parent.context)
.inflate(R.layout.layout_item, parent, false)
return MyViewHolder(view)
}
override fun getItemCount() = arrayList.size
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.number.text = arrayList[position].toString()
}
class MyViewHolder(view: View) :
RecyclerView.ViewHolder(view) {
val number = view.findViewById<View>(R.id.singleNumberId) as TextView
}
}
Here is a small example I have of registering a click for a RecyclerView adapter item:
class PatientListAdapter : ListAdapter<Patient, PatientListAdapter.PatientViewHolder>(co.za.abcdefgh.viewmodels.PatientListViewModel.DiffItemCallback) {
// this property will be used to set the onclick callback for the entire adpater
var onPatientSelectedCallback: PatientSelectedCallback? = null
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): PatientViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_patient, parent, false) as View
return PatientViewHolder(view)
}
override fun onBindViewHolder(holder: PatientViewHolder, position: Int) {
holder.view.item_patient_name.text = getItem(position).toString()
holder.view.item_patient_folderNumber.text = getItem(position).folderNumber
// lets set our on click for each viewholder here
holder.view.item_patient_info_card.setOnClickListener {
// the secret sauce .... getItem(holder.adapterPosition)
onPatientSelectedCallback?.onPatientSelected(getItem(holder.adapterPosition))
}
}
class PatientViewHolder(val view: View) : RecyclerView.ViewHolder(view)
// interface which defines a method signature that will called when a item in the adpater is selected
interface PatientSelectedCallback {
fun onPatientSelected(patient: Patient)
}
}
and then wherever you use the adapter after instantiating simply do:
val viewAdapter = PatientListAdapter()
viewAdapter.onPatientSelectedCallback =
object : PatientListAdapter.PatientSelectedCallback {
override fun onPatientSelected(patient: Patient) {
// do something with the chosen item
patientViewModel.setPatient(patient)
}
}

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

RecyclerAdapter is creating multiple view back to back

I am using recyclerview to display images in cardview. When I load images from the url. It loads well from first time but when It loads for second time it is not displayed properly.
How to fix it? Here is the code
override fun getItemCount(): Int {
dataList = FlickrDBOperation.dataArray
if (dataList!!.isEmpty())
return 0
Log.e("count",dataList!!.size.toString())
return dataList!!.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
var itemView: View = LayoutInflater.from(parent.context).inflate(R.layout.imagelist_row,parent,false)
var viewHolder: FlickrAdapter.Holder = FlickrAdapter.Holder(itemView)
return viewHolder
}
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.image_name.text = dataList!!.get(position).name
Picasso.with(context).load(dataList!!.get(position).preUrl).into(holder.row_image)
}
class Holder(itemView: View?) : RecyclerView.ViewHolder(itemView) {
val row_image: ImageView
val image_name: TextView
init {
row_image = itemView!!.findViewById<ImageView>(R.id.row_image)
image_name = itemView!!.findViewById<TextView>(R.id.image_name)
}
}

Categories

Resources