In the below code 'setlisteners' method is called every time,should'nt value of position change?
There is only one method'setlisteners'in which parameter position is passed,but when in log ,i see that whenever click event happens on my buttons,it shows correct position. should'nt position overwrite itself and show incorrect position because click event can happen at any time ,by that time value of position will have changed?
let's suppose 5 views have been binded and i click on the first
itemview,then should'nt it show me the last position held by the last
binded itemview?
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
setListeners(holder,position);
}
private void setListeners(ViewHolder holder, final int position) {
holder.updownarrow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.e("ddfg",""+position);
}
});
holder.leftlayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
position will hold the position of the last holder which was bind in onBindViewHolder, since my View does not call onBindViewHolder when i click on it,should'nt position hold the position last binded and show only that one position whenever any view gets clicked?
The position in onBindViewHolder is the same as the position in your adapter/data set. Your Click Listener is being set with the position the individual ViewHolder represented in your data set at the time of binding. Even though the click event isn't being called until after all your Views are binded, each function instance was already set up to use the corresponding position each view was set with.
If each View shared the same instance of a Click Listener, then you would only get the last position. But, each View holds its own instance of a Click Listener.
No, position will hold the position of the last holder which was bin in onBindViewHolder, since your View does not call onBindViewHolder when you click on it.
There are many possibilities to retrieve the correct position - for example if you set & get the position in the Views tag or search for the surrounding ViewHolder and check its position in the adapter or layout.
Dont use the position in onBindViewHolder. use this:
final int adapterPosition = viewholder.getAdapterPosition();
// do what ever with it
Related
I am trying to make a text change when a button located along with the text (layoutPasswd) in recycler view and to change it back if the button is again pressed.Like a password hiding button. The values to the adapter is from a static class object as arraylist. The problem occurring now is that the value for all the items (only for layoutPasswd) in recycler view is same.
public void onBindViewHolder(#NonNull final viewHolder holder, int position) {
holder.layoutUName.setText(users.get(position).getUserName());
pos = position;
holder.layoutPasswd.setText("********");
holder.btnViewChanger.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (holder.view1) {
holder.layoutPasswd.setText(users.get(pos).getPasswd());
holder.btnViewChanger.setText("hide");
holder.view1 = false;
} else {
holder.layoutPasswd.setText("********");;
holder.btnViewChanger.setText("Show");
holder.view1 = true;
}
}
});
You cannot rely on the ViewHolders or Views in a RecyclerView to hold any state, because they are recycled. Every time a view scrolls onto the screen, first it calls your onBindViewHolder function to update the contents of that ViewHolder to match the data.
Any configuration you set on the views or the ViewHolder instance in onBindViewHolder cannot be relied on to stay the same if the view scrolls off the screen, because the original ViewHolder might be recycled to be used for some other data, and when it scrolls back on screen, you might be looking at some other view that has been recycled from other data that just scrolled off the screen.
So if your views have configuration that you want to "stick", you have to back it up when you change it, and restore it in onBindViewHolder. The way you accomplish this will depend on how you are managing the data that you pass to the adapter.
If you can modify your User class, you can add a Boolean to it that stores whether it should show the password. Then in your onBindViewHolder, you restore the state based on this Boolean. And you also update this Boolean when the state changes.
I also updated the way the click listener works to simplify it for toggling. I removed the pos = position line, because almost certainly that is not something you should be doing.
public void onBindViewHolder(#NonNull final viewHolder holder, int position) {
final User user = users.get(position)
holder.layoutUName.setText(user.getUserName());
holder.layoutPasswd.setText(user.isShowPassword() ? user.getPasswd() : "********");
holder.btnViewChanger.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
user.setShowPassword(!user.isShowPassword());
holder.layoutPasswd.setText(user.isShowPassword() ? user.getPasswd() : "********");
holder.btnViewChanger.setText(user.isShowPassword() ? "hide" : "show");
}
});
// ...
}
If you cannot modify the User class, this is more complicated. Then the adapter should have its own ArrayList<Boolean> to store the state by position index, but you need to keep this list at least as long as the data that is bound, and reset everything to false if the whole list of data is refreshed.
I am trying to change the color of the first row in "monitoredpatientlsit" which is a list view. The change should happen if I click on an item in allpatientslist and it already exists in monitoredpatient. So suppose I click on item 3, which also happens to be item 3 in monitoredpatients. I expect the code to change the color of the first view in monitored patient to red. However it doesn't, and I think it has to something to do with "notifydatasetchanged" method, because when I comment it out, it would work the way I want it to, but when I put it back to the code, there are no changes in the color at all. Any Idea why?
allpatientslist.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (selectedpatients.contains(allpatients.get(position))){
selectedpatients.remove(selectedpatients.indexOf(allpatients.get(position)));
refomatToRed();
monitoredPatientListAdapator.notifyDataSetChanged();
view.setBackgroundColor(Color.parseColor("#F4F6F1"));
}else{
view.setBackgroundColor(Color.parseColor("#B0D880"));
selectedpatients.add(allpatients.get(position));
monitoredPatientListAdapator.notifyDataSetChanged();
}
}
});
public void refomatToRed(){
monitoredpatientslist.getChildAt(0).setBackgroundColor(Color.RED);
}
Edit:
I fixed it by just implementing the change of color inside the adaptor, since every time an item is removed by view due to the scrolling feature, the item is actually destroyed and will lose any formatting down to it.
When you call notifyDataSetChanged() on the adapter, the list is re-drawn. Changing the color of the first child has no effect because this child is destroyed at the following line.
Try inverting the lines like this:
monitoredPatientListAdapator.notifyDataSetChanged();
refomatToRed();
I need to get the ID of the current item in my adapter but it only returns the id of the last added item.
I can't figure out how to get around this.
#Override
protected void onBindViewHolder(ViewHolder holder, final int position, CardViewModel cardViewModel) {
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("id: ",cardViewModel.getId());
}
});
}
Click on the 1st, 2nd and 3rd item in order and output:
3
3
3
Must be:
1
2
3
My button isn't for my adapter view so I can't use: holder.button
Thanks...
EDIT:
When I click the button, I need to get the name on the cardView, but each time it returns the name on the last card.
Instead of getting cardViewModel.getId() use position variable it will return current position of item click.
You need to use ArrayList or List of CardViewModel cardViewModel as parameter then you will be able to get id of clicked item by
You need to pass List of CarViewModel as follows:
protected void onBindViewHolder(ViewHolder holder, final int position, ArrayList <CardViewModel> cardViewModelList)
You can retrieve the id of the current item clicked as follows:
CardViewModel cardviewModel = cardViewModelList.get(position)
This will give you expected result:
Log.d("id: ",cardViewModel.getId());
In my onBindViewHolder of my RecyclerView.Adapter<SearchAdapter.ViewHolder> when user clicks on cardview a button becomes visible. But when I'm scrolling recyclerview some other items buttons are shown as visible too. Why is this happening?
this is my code:
#Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
viewHolder.card.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (viewHolder.content_layout.getVisibility() == View.VISIBLE) {
viewHolder.content_layout.setVisibility(View.GONE);
viewHolder.address.setMaxLines(2);
viewHolder.attribute.setMaxLines(2);
} else {
viewHolder.content_layout.setVisibility(View.VISIBLE);
viewHolder.address.setMaxLines(8);
viewHolder.attribute.setMaxLines(8);
}
}
});
...
}
Once you start scrolling down the list your views get recycled. This means a previously inflated ViewHolder (some that gets created in onCreateViewHolder) is reused.
So what you have to do is to remember the clicked positions (e.g. via a SparseBooleanArray) and check in onBindViewHolder whether the view should be visible (previously clicked) or not.
You can find a basic usage example of the SparseBooleanArray in this StackOverflow post
The 'other' visible items buttons are the ones using the same viewholder that was modified in the callback. So because viewholders (and views) are recycled :
They should only store information that can be retrieved each time the viewholder is bound to a position.
Anything that may be changed in the views state should be refreshed in onBindViewHolder()
In your case you should store the 'is selected' somewhere else and reset the visibility and maxlines in onBindViewHolder() (not only in the callback)
Good idea is to make a class object with all data you need for one item in recycler view, also add there one boolean isItemWasClicked and inside onBindViewHolder() check this boolean and make buttons visible or not.
For example:
public class OneItemOfList{
int priceToDisplay;
String name;
String date;
boolean wasClicked;
}
public class YourAdapter extends RecyclerView.Adapter<OneItemOfList.ViewHolder> {
ArrayList<OneItemOfList> items;
...
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
viewHolder.view.setText(items.get(position).name);
if (items.get(position).wasClicked)
viewHolder.button.setVisible(View.VISIBLE);
else
viewHolder.button.setVisible(View.GONE);
viewHolder.view2.setOnClickListener(...
OnClick(...){
items.get(position).wasClicked = !items.get(position).wasClicked;
});
}
...
}
create an array for example Boolean array, and when each position clicked, set true in same position of array. and in onBindViewHolder check if that array[position] is true set that item visible if.
I am trying to remove items from a RecyclerView from my adapters onBindViewHolder.
When I call this...
public void removeDropFromView(int position) {
data.remove(position);
notifyItemRemoved(position);
}
...my animation shows, but it will not allow the adapter position to update (the new position 0 becomes position 1).
When I call this...
public void removeDropFromView(int position) {
data.remove(position);
notifyItemRemoved(position);
notifyDataSetChanged();
}
...the item is removed, the position of all my items are updated, but it completely skips the animation.
Some have said my troubles are coming from this not being possible in the onBindViewHolder, but I have tried all of this in the ViewHolder's onClick with the same results.
How can I get the animation to show, while also keeping all of the data in proper order?
Thanks for the help!
Before deleting the data, get its viewholder, then call the setIsRecyclable(false) of the viewholder. Check this, line 102. It uses swipe to delete.