I've a recyclerview and I want to delete a recyclerview item.
fun deleteItem(position: Int){
ModelList.removeAt(position)
notifyItemRemoved(position)
notifyDataSetChanged()
}
override fun onBindViewHolder(holder: myRecyclerViewAdapter.ViewHolder, position: Int) {
holder.bind(ModelList[position])
holder.itemView.listRowDelete.setOnClickListener {
deleteItem(position)
}
I deleted items with this method but because of these items are in sqlite database they all come back when I restard the app. I can't reach positions from my mainactivity and I cant reach database inside of adapter. How can I delete specific item from sqlite database which I want to. Please help thank you
For onClickListener , Don't rely on position from the onBindViewHolder
Instead get the position from holder like this
holder.itemView.listRowDelete.setOnClickListener {
deleteItem(holder.adapterPosition)
}
InClickListeners and things that happen afterwards , get the accurate position with adapterPosition , hover over the adapter position to see the quick documentation !
If you want to call database method , I would suggest you to not call it in Adapter & rather have methods to set custom listeners to adapters which can be called with the victim (item supposed to be deleted)
have a function like this in your adapter
Define the listener property like this
private val myListener:MyCustomListenerInterface = MyCustomListenerInterface{}
Have a setter function to set listener
fun setListener(l:MyCustomListenerInterface){
myListener = l
}
interface MyCustomListenerInterface{
fun onDelete(model)
}
Then in your onClickListener call the onDelete of the custom listener interface like this
holder.itemView.listRowDelete.setOnClickListener {
myListener.onDelete(modelItem)
}
Now When Initializing the adapter for recycler view in fragment/activity , set the listener !!
Related
enter image description hereI have almost 250 patent item which i want to show in a parent recyclerview. After item click of parent recyclerview it will show over hundreds of data under each parent item in another recycler view. How can i do it like this picture.
I would do it this way. Create an interface:
interface AdapterContract{
fun onListItemClick(id: String) //whatever the parameter is
}
When you instantiate the Adapter in your Fragment1, you can implement this interface:
class Fragment1 : Fragment(), AdapterContract{
...
//when instantiating the adapter:
private val adapter: MyAdapter by lazy{
MyAdapter(this //for the interface)
}
//override the method of Adapter contract
}
Your adapters constructor should look like this: MyAdapter(adapterContract: AdapterContract) : ListAdapter<MyAdapter.MyViewHolder>(Diff_Util_SOME_OBJECT)
Than in the adapters ViewHolder:
itemHolder?.setOnClickListener{
adapterContract.onListItemClick(someId)
}
Now in your override method in Fragment1:
override method onListItemClick(id: String){
//pass this id to the next opening fragment (like Bundles or navargs)
//init `Fragment2` which is going to have all items, that belong to `Fragment1` selected item
}
Load the data :)
I hope you are using a database or something. Should work with hard coded data structures as well, but just to be safe :).
I am using a RecyclerView to show list of products in my app, I need to group the product based on aisle. while the data are fetched for the first time in the list, the products are grouped correctly with respect to aisle. When we scroll the view, the aisle group divider is shown for the wrong item and the divider gets restored to correct position once the onBindViewHolder gets refreshed automatically.
MyAdapter.class
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
itemsGrouping(pickItem, pickItemView, holder.adapterPosition)
}
private fun itemsGrouping(pickItem: PickItem, pickItemView: View, adapterPosition: Int) {
//Based on some condition
if(SomeCondition)
itemDivider(pickItemView,true)
else
itemDivider(pickItemView,false)
}
private fun itemDivider(v: View, boolean: Boolean) {
if(boolean) {
v.visibility = View.VISIBLE
} else {
v.visibility = View.GONE
}
}
Well, you should know that the view holders are reused in the RecyclerView, so it's probable not the right idea to try to determine the visibility of the divider in onBindViewHolder. I would recommend using item decorator for dividers. Here's the question and answer for that
How to add dividers and spaces between items in RecyclerView?
The problem is RecyclerView recycles previous views in order to be efficient.
I guess "SomeCondition" contains artifacts which are from previous holders.
So at
itemsGrouping(pickItem, pickItemView, holder.adapterPosition)
you should get pickItem and pickItemView from newly bound holder. You should use like
pickItemView = holder.findViewById(R.id.pickItemView);
Or consider using DataBinding Library
Here is a good example (it's in Kotlin) : DataBoundListAdapter
Once you extend your adapter to DataBoundListAdapter and override bind() method, everything inside bind is executed for every row, so you won't get repeated results.
Note : notice "executePendingBindings()"
override fun setAdapter() {
adapter = WaybillAdapter(items, activity!!, this, recyclerView)
recyclerView.adapter = adapter
}
I am updating my adapter when I should add items for my recycler like
override fun addWaybills(list: ArrayList<Data>) {
items.addAll(list)
adapter.setLoaded(true)
}
Add my position of an recycler is jumping to start.
My question is how should update adapter by not changing current position?
In your activitie's onCreate or fragment's onCreateView create initialize your adapter with empty ArrayList and later when you fetch your data add items to your arraylist and call adapter.notifyDataSetChanged()
I am having trouble debugging a recycler view issue. Here I am trying to delete an item from recycler view using the following method:
when I click on the card (one that is to be deleted), it will store the title of a card in shared preference. (so that in future when I open the app I will know what cards are not to be displayed)
Then I am calling a method which checks if the title in shared preference matches the title of the card and make ArrayList consisting of cards which are not present in shared preference.
Now I am using this ArrayList to fill cards using notifyDataSetChanged
My code is as Follows.
Adapter.
class LocalAdapter(var localCardsInfo : ArrayList<LocalModel>?,var fragment: LocalListingFragment) : RecyclerView.Adapter<LocalHolder>() {
fun refreshDataOnOrientationChange(mLocalCards : ArrayList<LocalModel>?){
if (localCardsInfo!!.size>0)
localCardsInfo!!.clear()
localCardsInfo = mLocalCards
notifyDataSetChanged()
}
override fun getItemCount(): Int {
return localCardsInfo!!.size
}
override fun onBindViewHolder(holder: LocalHolder, position: Int) {
if (localCardsInfo!=null)
holder.updateUI(holder.adapterPosition,localCardsInfo!![holder.adapterPosition])
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LocalHolder {
val card_a : View = LayoutInflater.from(parent.context).inflate(R.layout.card_local_a,parent,false)
return LocalHolder(card_a,fragment)
}
}
ViewHolder
class LocalHolder(itemView : View,val fragment: LocalListingFragment) : RecyclerView.ViewHolder(itemView),OpenedLayoutManagerLocal{
fun updateUI(position : Int , localModel: LocalModel){
moveButton.setOnClickListener({
archived()
})
private fun archived(){
ALL_STATIC_CONSTANTS_AND_METHODS.addToArchive(fragment.activity!!,model!!.roomName,model!!.deviceName)
itemView.archiveLayout.visibility = View.GONE
itemView.elevateLayoutLocal.visibility = View.GONE
fragment.mAdapter!!.refreshDataOnOrientationChange(ArrayList(LocalDataService.ourInstance.getNonArchivedItems(fragment.activity!!)))
}
For example, if I have six item in recycler view and when I delete one I can see 5 items on the list while debugging, even onbind is called 5 times but only 4 items are displayed.
I tried my best to debug it yet failed to find a solution and I can't think about what to do next. I tried notifyItemRemoved and notifyItemRangeInserted also but still, I am facing the same issue. It would be a great help if someone can suggest a possible cause of such issue.
Thank you.
Edits:
I just experimented by adding dummy button in fragment where recycler view is placed. An to my surprise if I delete element here just element at that position gets deleted and there is no case of disappearing items. So it seems like problem happens when I delete an item from view holder. So I guess I got the cause of the error but can't think of a way to implement it because the original button is in the card so I am forced to use delete procedure inside view holder. Please do suggest if you have any suggestions. Code that I added in fragment is as follow:
v!!.findViewById<Button>(R.id.delete_button).setOnClickListener({
try {
val model = LocalDataService.ourInstance.getNonArchivedItems(activity!!)[0]
ALL_STATIC_CONSTANTS_AND_METHODS.addToArchive(activity!!,model.roomName,model.deviceName)
mAdapter!!.refreshDataOnOrientationChange(ArrayList(LocalDataService.ourInstance.getNonArchivedItems(activity!!)))
}catch (e : Throwable){}
})
Here on this new dummy button click, it deletes the first item in the list gets deleted while on clicking the button on RecyclerView card it deletes one item and then one more item gets disappeared.
I'm doing notifying data-set-change inside of Adapter.
like,
fun setItems(items: MutableList<IMyModel>) {
list = items
notifyDataSetChanged()
}
fun updateItems(pos: Int: item: IMyModel) {
list[pos] = item
notifyItemChanged(pos)
}
But in many tutorials, I can see they do outside of the Adapter.
adapter.setItems(items)
adapter.notifyDataSetChanged()
So I just wondered if there is any reason I should notify that outside of Adapter? like a bad practice?
Usually, you should call notify directly after changing the dataset, no matter in which class that change occurs.