RecyclerView update item's text and icon OnClick - java

I implemented a RecyclerView which is populating some records
It is populating appointments and appointments have approved and not approved status.
I implemented an approved button so that onclick the approved status for that appointment is updated on the database and also icon and text is changed of that particular displayed record.
but for some reason in onclick when i am updating the text and icon it is not changing dynamically, but the request to database is working since i can see the change in database, and when i reload the whole data set it shows the changes appropriately.
Here is the code
#Override
public void onBindViewHolder(final AppointmentsHolder holder, final int position) {
final AppointmentObject appointment = appointmentList.get(position);
//binding the data with the viewholder views
holder.patient_name.setText(appointment.patient_name);
holder.app_date.setText(appointment.appointment_date);
if (appointment.descr.length() > 30){
holder.app_descr.setText(String.valueOf(appointment.descr.substring(0,30)));
}else {
holder.app_descr.setText(String.valueOf(appointment.descr));
}
if (appointment.approved_status.equals("0")){
holder.approved_status.setTextColor(Color.RED);
holder.approved_status.setText("Not Approved");
holder.app_pa_approve.setImageResource(R.mipmap.approveicon);
}else {
holder.app_pa_approve.setImageResource(R.mipmap.approvedicon);
holder.approved_status.setTextColor(Color.GREEN);
holder.approved_status.setText("Approved");
}
holder.app_pa_relative.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mOnClickListenr.clicked_On_appointment(appointment);
}
});
holder.app_pa_approve.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.approved_status.setTextColor(Color.GREEN);
holder.approved_status.setText("Approved");
holder.app_pa_approve.setImageResource(R.mipmap.approvedicon);
notifyDataSetChanged();
mOnClickListenr.aprove_appointment(appointment, position);
}
});
}
Here is the onBindViewHolder where i am trying to change the data dynamically onclick.
mOnclickListiner.aprove.appointment is just a listiner which i am implementing in the activity to pass data to activity
public interface OnClickListener{
public void clicked_On_appointment(AppointmentObject appointment);
public void aprove_appointment(AppointmentObject appointment, int position);
}
The mOnClickListenr.aprove_appointment(appointment, position); inside the OnClcick is executing so i know that the setText and setImageResource are executing as well but for some reason change in not showing in the view.
As you can see i am using notifyDataSetChanged(); properly.
What might be the cause of this behavior ?
As you can see even when i am clicking on the imageButton no dynamic change
only when i reload the arraylist it shows the changed, which means change is happening in database and onclick is executing

You have to set approved on the appointment When you call notifiyDataSetChanged it is rebinding all the views and the appointment is still marked unapproved
holder.app_pa_approve.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
appointment.setApprovedStatus(1);
holder.approved_status.setTextColor(Color.GREEN);
holder.approved_status.setText("Approved");
holder.app_pa_approve.setImageResource(R.mipmap.approvedicon);
notifyDataSetChanged();
mOnClickListenr.aprove_appointment(appointment, position);
}
});

Related

How to make the holder in if else statements of onClick method work without final in argument Viewholder in arguments of onBindViewHolder method

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.

Correct creation of a dialog alert via the long click of a recyclerview?

My question may seem special but I need your help with how I create dialog boxes for each element of a recyclerview.
Indeed my fragment contains a recycle view with X elements, and I want that, when the user presses one of the elements for a long time, a dialog box opens and asks him "Are you sure you want to delete this element?" and two buttons "Yes" and "Cancel".
I have succeeded in doing so, but I especially want to know if my way of doing it is the right way, in order to avoid acquiring bad programming reflexes.
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
PlaceViewHolder Pholder =(PlaceViewHolder) holder;
Glide.with(mContext)
.load(mPhotoList.get(position))
.fitCenter()
.into(((PlaceViewHolder) holder).mPlace);
Pholder.mPlace.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
switchContext(mPhotoList.get(position));
}
});
Pholder.mPlace.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
new MaterialAlertDialogBuilder(mContext)
.setTitle(R.string.profile_photos_dialog_box_title)
.setMessage(R.string.profile_photos_dialog_box_message)
// Specifying a listener allows you to take an action before dismissing the dialog.
// The dialog is automatically dismissed when a dialog button is clicked.
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Continue with delete operation
}
})
// A null listener allows the button to dismiss the dialog and take no further action.
.setNegativeButton(android.R.string.no, null)
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
return true;
}
});
}
As you can see, every time I call onBindViewHolder I add an onClick listen and it is in this last one that I create the dialog box
Moreover, since my message is always the same, should I turn to a contextmenu, even if I find the format of this menu not very adapted to the choices and possible action?
I'm honestly not sure what the "best practice" may be, but I've done this before. How I've done it is, instead of creating the dialog and performing the action inside the longclick handler within onBindViewHolder, I create a custom listener interface so I can pass
relevant data back to the caller. Like this:
MyListListener
public interface MyListListener {
// Where "index" can be the index of the item, or the object represented by the list item at "index"
void onItemLongPressed(int index);
}
I'll pass that into the constructor of my custom RecyclerView.Adapter like this:
MyAdapter
public class MyAdapter extends ... {
private MyListListener listener;
public MyAdapter(MyListListener listener) {
this.listener = listener;
}
}
Then inside onBindViewHolder
MyAdapter / ViewHolder onBindViewHolder
Pholder.mPlace.setOnLongClickListener(new
View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
listener.onItemLongPressed(position);
}
});
And then my fragment or activity will implement the listener interface:
MyActivity
public class MyActivity extends ... implements MyListListener {
#Override
public void onItemLongPressed(int index) {
// Show the dialog and do things
}
}
I personally like this because it means my adapters are more isolated. They don't do much outside of showing a list of things. The logic behind those "things" ultimately lives elsewhere. It's easier to unit test this way as well, because you can mock the listener interface.

Items of RecyclerView are not showing correctly

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.

Different click events for different objects on ListView row

I am trying to implement different click event listeners for different sections of a single row of listview.
For example:
I have made 2 different linear layouts for the 2 different sections. Any particular way to implement the onclick listeners for the 2 different layouts?
Please suggest any other process of doing this if you have in your mind.
P.S. I cannot define click events in the custom adapter, as the Activity needs to fire the events from it with adding some data to it.
create static method in your Activity :
public static void YourMethod()
{
}
create onClickListener in adapter and call activity method.
holder.YourLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
YourActivity.YourMethod();
}
});
You can implement click Listeners in your adapter class like this-
layout1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
layout2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});

get the button that fired the event

well at school every week we are making a calculator each week on a different platform (wp7,javascript,iphone,android), today it's android, so i have a method that receives all the keystrokes and depending on the value of the key my class do something to get the value of the button in c# is the sender parameter , in javascript this , in android??
private OnClickListener leclicke= new OnClickListener() {
public void onClick(View v) {
//get the id of the button that fired the click event
//findViewById(R.id.???);
} };
thank you.
private OnClickListener leclicke= new OnClickListener() {
public void onClick(View v) {
//get the id of the button that fired the click event
int id = v.getId();
} };
then you must check if this view has an id or not using this
if(id == View.NO_ID){
//this view does not have an id
}
else{
//the view has an id
}
Call the method getId()
v.getId();
If you want to use the same OnClickListener for all buttons, then you can do something like this:
Button b1 = (Button)findViewById(R.id.button1);
Button b1 = (Button)findViewById(R.id.button2);
private OnClickListener leclicke= new OnClickListener() {
public void onClick(View v) {
if(v.equals(b1)) {
// handle button 1
} else if(v.equals(b2)) {
// handle button 2
} // etc.
} };
But this is a little clunky. Another thing you can do is specify a separate on click method for each method by setting the on click property for the button in the UI Designer, and then declaring that method in your Activity, e.g. public onClickButton1(View v);

Categories