Hi I am using Recyclerview with Adapter in my Android application. In my app I have product listing screen, where user can set the quantity in every list item. I am using two imageviews for increment and decrement the quantity. Now issue is , if I make 2 quantity for first product and 4 for second product, total count should be 6 for my cart screen, but I am not able to get total count. Following is my code for adapter, can anyone help me ?
class ProductAdapter(private val cellClickListener: CellClickListener) : RecyclerView.Adapter<ProductViewHolder>() {
var productsList = mutableListOf<Product>()
var cartList = mutableListOf<Product>()
/* fun setMovieList(movies: List<MobileList>) {
this.movies = movies.toMutableList()
notifyDataSetChanged()
}*/
fun addData(data:List<Product>){
productsList.addAll(data)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = AdapterLayoutBinding.inflate(inflater, parent, false)
return ProductViewHolder(binding)
}
override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
val products = productsList[position]
holder.binding.name.text = products.name
holder.binding.price.text = products.price
Glide.with(holder.itemView.context).load(products.image_url).into(holder.binding.imageview)
var cartCount : Int=0
holder.binding.cartPlus.setOnClickListener {
cartCount++
holder.binding.cartValue.text = cartCount.toString()
cartList.add(products)
//println(cartList)
}
holder.binding.cartMinus.setOnClickListener {
cartCount--
holder.binding.cartValue.text = cartCount.toString()
cartList.remove(products)
// println(cartList)
}
fun updatedData( updatedcartList:List<Product>){
cartList.addAll(updatedcartList)
}
holder.itemView.setOnClickListener {
cellClickListener.onCellClickListener(products,cartCount)
}
}
override fun getItemCount(): Int {
return productsList.size
}
}
class ProductViewHolder(val binding: AdapterLayoutBinding) : RecyclerView.ViewHolder(binding.root) {
}
interface CellClickListener {
fun onCellClickListener(data: Product,count:Int)
}
use cartlist.size() for total count.
and for using in activity define this in Adapter class:
class ProductAdapter(private val..
{
...
fun getCartSize():Int {
return cartlist.size()
}
...
}
and in the activity you can use :
adapter.getCartsize()
since you are just passing the list into the recycleview and want to use it in fragment or activity. I would suggest using the list to get the total count for your items instead. You can use sumOf to your list.
you can use it like this before you pass to recyclver view.
val totalCount = list.sumOf { it.quantity }
or
txtView.text = list.sumOf {it.quantity}.toString()
Related
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()
}
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
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
I have a recyclerview that shows items that were bought and to be bought. The items are sorted by purchase date which can be in the past and future.
I want to add a separator between the last "past" item and the first "future" one. I know I should add a decorator like the following:
DividerItemDecoration decoration = new DividerItemDecoration(Objects.requireNonNull(getActivity()), VERTICAL);
rvItems.addItemDecoration(decoration);
The decorator is a thin horizontal line that is shown between all items. How can I make the decorator a text, something like "↓ past items ↑ future items" and to be visible only between the relevant items?
Thanks
In such a case I think it is preferable to have a recycler view adapter that supports multiple types of items.
The separators that you call, will be sections
Example:
sealed class Item {
data class Section(val sectionText): Item
data class Purchases(.......): Item
}
Adapter:
class MyAdapter(private val items: List<Item>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
const val TYPE_PURCHASES = 0
const val TYPE_SECTION = 1
}
override fun getItemViewType(position: Int): Int {
val type = when (items[position]) {
is Items.Section -> TYPE_SECTION
// other types...
else -> TYPE_PURCHASES
}
return type
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val viewHolder: RecyclerView.ViewHolder = when (viewType) {
TYPE_PURCHASES -> {}
// other view holders...
else -> {}
}
return viewHolder
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {
when(holder) {
is SectionViewHolder -> bindSectionViewHolder(holder)
is PurchasesViewHolder -> bindSectionViewHolder(holder)
}
}
override fun getItemCount() = items.size
}
Then create the corresponding ViewHolders for each type and instantiate them at onCreateViewHolder.
And when you pass the list of Items in the adapter you should have logic that adds either a Section or an Item to the list.
Similar example from one of my projects:
.subscribeBy(onSuccess = {
val list = mutableListOf<IViewModel>()
val previousBookingDate = Calendar.getInstance()
val bookingDate = Calendar.getInstance()
it.data?.fold(Date(0)) { previousHeader, bookable ->
previousBookingDate.time = previousHeader
bookingDate.time = bookable.getStartDate()
val timeIgnoringComparator = DateUtils.TimeIgnoringComparator()
if (timeIgnoringComparator.compare(previousBookingDate, bookingDate) != 0) {
list.add(SectionViewModel(bookable.getStartDate(), isDefaultSearch || isDefaultCenterSelected()))
}
list.add(BookableItemViewModel(bookable))
bookable.getStartDate()
}
searchResultsLiveData.value = list
}, onError = {
isError.setValue(Event(Pair(null, Runnable { performSearch() })))
})
I suggest taking a look at this library to quickly make lists with different item types.
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?) {
...
}