I'm working on a screen where I have to display twice the same collection of items. I'm using two RecyclerView's each with a different instance of the same Adapter
Besides the two RecyclerViews in the screen I have a continue button to go to the next screen.
I want to put some constraints on the button so that the user will not be able to proceed to the next screen if he doesn't select an item from each RecyclerView
The following method is called when the user taps on an item.
fun setSelection(item: Item) {
list.forEach {
it.isSelected = false
if (it.id == item.id) it.isSelected = true
}
notifyDataSetChanged()
}
And to verify if the list has at least one selected item I use this:
fun isAnySelected(): Boolean {
return list.any { item -> item.isSelected }
}
In the button's clickListener I added this verification:
if ((first_recycler.adapter as CustomAdapter).isAnySelected() &&
(second_recycler.adapter as CustomAdapter).isAnySelected())
{ //go to next screen }
else{
// select an item before continue }
My problem is that if I select an item only from one RecyclerView, when I tap on the continue button both of them will return true.
Why does (second_recycler.adapter as CustomAdapter).isAnySelected() returns true if only
(first_massage_display.mdl_recycler.adapter as MassageAdapter).setSelection(item)
Is called?
Sounds like the lists in each adapter are referencing the same list item instances. This could be because you passed the same list to both adapters, or simply that you have not created distinct list item instances in each list. If two lists reference the same list items, than changing a list item in one list also changes it in the other.
I recommend making Item a data class so you can easily make copies. Then create a copy of the list where every member of the list is also copied, and pass that to the second Adapter. You can use map() to do both steps of copying the list and the items within it all at once:
val secondList = firstList.map(Item::copy)
Related
i need help.
I wanted to change button visibility in any other item inside recyclerview to dissapear, so other button except item that i selected become invisible ...
The trigger is when item get clicked/selected then any other item's button goes invisible/gone except the one i clicked... how do i do that? im using Kotlin btw.
Here is where i want to implement it inside "if (!b)" (if possible) in onBindViewHolder:
holder.mainLayout.setOnClickListener {
if (!b){
b = true
holder.addsLayout.isVisible = true
} else {
b = false
holder.addsLayout.isVisible = false
}
}
Edit:
Here is my viewholder:
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val itemTitle: TextView = itemView.findViewById(R.id.itemTitleRecordLocal)
val itemDelete: Button = itemView.findViewById(R.id.recordlocalDelete)
val itemTranscribe: Button = itemView.findViewById(R.id.transcribelocalTxt)
val mainLayout: RelativeLayout = itemView.findViewById(R.id.recordlocalTitleDateLayout)
val addsLayout: RelativeLayout = itemView.findViewById(R.id.addsRecordLocalLayout)
}
I just updated my code earlier, so its not button anymore but layout, but i think its similar bcuz i just need to change the visibility.
itemDelete and itemTranscribe are buttons that i want to hide, and i put it in addsLayout.
mainLayout is the item where i need to click to be selected.
As per your code, I understand that you are passing any data class to the recycler view adapter of type suppose myData
Here is what you can do:
Take one boolean variable in your data class item, say isSelected, default value will be false.
Then whenever you get click of your mainLayout, change the value of that boolean.
Check condition according to that boolean and manage visibility according to that.
Also, best practice is you manage your clickevents in activity/fragment. So my suggestion is give callback of click of mainLayout to the activity/fragment and perform the next operations there only.
After that, in activity/fragment you need to make for loop and make isSelected false for all data except the one you selected.
For example in my case I have one music player recyclerview and need to change icon if one is being played then all others should be pause. So I need to change icon based on that condition. My data class contain one variable isPlaying.
In adapter:
// Inside view holder
if (data.isPlaying) {
// set image of play
} else {
// set image of pause
}
In on click, I have make one mutable live data and observe that in activity/fragment and get callback of click there.
Inside activity/fragment:
adapter.getListItems().forEach {
it.isPlaying = false
}
data.isPlaying = true
notifyDataSetChanged()
I have a list of items in a RecyclerView that fetched from the Firestore database. Whenever I add, delete or edit an item I call respective function to make changes in the Firestore and then I call the getProductListFromFireStore() to update the RecyclerView. But the problem that I have is every time I make these changes, I go to the top of the list. I want to stay where I was even after adding/deleting or editing. How can I do that?
I didn't have the notifyDataSetChanged() in my code earlier, this was added to check if this will do the job, I do not know if I have done this right.
The following function is called when the product list is successfully fetched from the Firestore
fun successProductsListFromFireStore(productsList: ArrayList<Product>) {
hideProgressDialog()
if (productsList.size > 0) {
binding.rvMyProductItems.layoutManager = LinearLayoutManager(activity)
binding.rvMyProductItems.setHasFixedSize(true)
val adapterProducts =
MyProductsListAdapter(requireActivity(), productsList, this#ProductsFragment)
binding.rvMyProductItems.adapter = adapterProducts
adapterProducts.notifyDataSetChanged()
} else {
binding.rvMyProductItems.visibility = View.GONE
binding.tvNoProductsFound.visibility = View.VISIBLE
}
}
I think u need to use DiffUtill. It automatically update only changed items and if at least one item on screen was not changed then u will stay on the same position
I am working on a project. The scenario is that I am receiving a list of items from server. Before setting it to recycler-view adapter I am checking that is the items are deleted by the user or not, if the items deleted then data of deleted items like itemId, deletedItemsCount, customerId will store in local Database. Then getting the deleted items of specific product from database and subtract it from the total items receiving from server and then update the specific item in the list and then setting it to adapter.
The easy way to understand the scenario is (steps)
receiving list of products from server
by getting data from local database check if the items deleted or not
if yes, then store the deleted items of specific product in local database
get the deleted items from local database and subtract it from items receiving server
update that server list before setting it to adapter
then set it to adapter
The Problem is that when the user delete the items by clicking deleted icon, it updates all product's items in the list. It should update that specific product's items of which the user clicked on the delete icon.
The screenshots and the code snippets attached for your reference.
Any type of suggestion would be helpful.
Thank you.
mainLocalViewModel.getAllDeletedItems().observe(viewLifecycleOwner, Observer {
viewModel.customerOrdersList.clear()
for (items in viewModel.serverCustomerOrderList) {
viewModel.customerOrdersList.add(items as OrderItemModel)
}
if (it != null && it.isNotEmpty()) {
for (itemsInDb in it) {
for ((index, itemsInServer) in viewModel.customerOrdersList.withIndex()) {
if (itemsInDb.itemId == itemsInServer.itemId && itemsInDb.customerId == viewModel.customerId) {
val totalInServer = itemsInServer.itemTotal
val totalInDb = itemsInDb.deletedItems
val remainingTotalItems = totalInDb?.let { it1 ->
totalInServer?.minus(
it1
)
}
viewModel.customerOrdersList[index].itemTotal = remainingTotalItems
if (::customerOrdersAdapter.isInitialized) {
customerOrdersAdapter.notifyItemChanged(index)
}
}
}
}
if (!::customerOrdersAdapter.isInitialized) {
customerOrdersAdapter =
CustomerOrdersAdapter(
requireContext(),
viewModel.customerOrdersList,
this
)
binding.apply {
recyclerViewCustomerOrder.apply {
layoutManager = GridLayoutManager(requireContext(), 2)
adapter = customerOrdersAdapter
}
}
}
}
Screenshot of the database structure
Screenshot of List receiving from server
Screenshot of user delete dialog
You may want to checkout DiffUtil like in this tutorial: https://www.raywenderlich.com/21954410-speed-up-your-android-recyclerview-using-diffutil
It's basically a class that compares every two items of your list and calculates the difference between the old and new list of data, that way if you only delete one item, it should just update that item from the list.
I'm using paging 3 to paginate my retrieved data.
I want to show a badge on the bottom navigation bar when a new item has been added to the recycler view.
imagine the user is watching recycler view in the situation below:
item A
item B
item C
now the user refresh the page using a swipe refreshing button which calls this function:
binding.refresh.setOnRefreshListener {
adapter.refresh()
}
and a new item is added, so the situation would be like this:
item D
item A
item B
item C
the new data would be propagated using this line of code:
viewModel.tickets.observe(viewLifecycleOwner, {
viewLifecycleOwner.lifecycleScope.launch {
adapter.submitData(it)
}
})
I want to show a badge at this moment. so I added an interface into my recycler paged list adapter like this:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
ticketUtils.hasNewItem(getItem(0)?.ticket_id)
holder.bind(getItem(position))
}
which checks what is the first item of the recycler view.
so I have implemented the body of this interface inside of my fragment like this:
override fun hasNewItem(itemID: String?) {
if (itemID.isNullOrEmpty())
return
if (itemID != firstTicketID)
MainActivity.mainUtils.newTicket(showBadge = true)
}
and MainActivity.mainUtils.newTicket is an interface that shows a badge on the desired icon.
but I have faced to the problem that it seems the recycler view won't be notified for new data till the user start scrolling. I mean the code written in onBindedViewHolder would be called just when the user scrolls the page.
how can I solve that?
viewModel.tickets.observe(viewLifecycleOwner, {
//Here you get to know that there's update in your Datalist
adapter.submitData(it)
})
When you insert a new data into the database, observe the data inserted by returning the id of the data. For example if you are using Room, it can return a long value, which is the new rowId for the inserted item.
Compare with existing ones and then you can show a badge if there's new data in the list.
I have a UI screen that creates CardViews that are saved to a RecyclerView list. Initially, there is no RecyclerView list because there are no CardViews initially. When the first CardView is saved it creates a RecyclerView list to show the CardView data.
I want to be able to test if there is a RecyclerView list already created. If not, launch the CardView activity for user input. If there is a RecyclerView list because a CardView(s) was previously created, then launch the RecyclerView activity. Any ideas on how to test for the existence of the RecyclerView list that I can use with an if/then statement to launch the correct activity?
If you are using a custom recycler adapter which i think you are(Provide more code if not) , Override getItemCount() function like
public int getItemCount() {
if(list!=null)
return this.list.size();
else
return -1;
}
And check the list size and do the functionalities in an if else
Edit
Check this in the activity like
if(adapter.getItemCount()>0)
{
//Do what you want
}
else
{
//Launch the activity
}