0

I am working on an app in which I fetched some data from a server in recyclerview. My recyclervew list layout contains 3 view [ textview(to display event name), edit icon (to edit names), one Switch to turn on/off the events. After some days of working i found a weird issue in this module i.e. when i turns on the 1st pos switch i automatically switches on the 10th switch in the recyclerview, if there are less than 10 names say if 9 then all switches works fine, then on the entry of the 10th they act as mentioned and so one turning 2nd on turns the 11th on. It's like recylerview is taking 10th position as 1st and so on.

I know it's weird but there must be problem with position or something. I googled but could not find anything on this. I am posting the code of my adapter/list item.xml. if you need any other thing i will post it later.

::::eventAdapter.java::::

public class eventAdapter extends RecyclerView.Adapter<eventAdapter.UsersViewHolder> {

    Context context;
    List<EventModel> userListResponseData;

    public eventAdapter(Context context, List<EventModel> userListResponseData) {
        this.userListResponseData = userListResponseData;
        this.context = context;

    }

    @Override
    public UsersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view = LayoutInflater.from(context).inflate(R.layout.event_list_items, parent,false);
        UsersViewHolder usersViewHolder = new UsersViewHolder(view);
        return usersViewHolder;
    }

    @Override
    public void onBindViewHolder(final UsersViewHolder holder, final int position) {
        // set the data
        final String eventName = userListResponseData.get(position).getEvent_name();
         holder.ed_eventname.setText(eventName);



        holder.ic_event_edit.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              FragmentActivity activity = (FragmentActivity)(context);
              FragmentManager fm = activity.getSupportFragmentManager();
              EditEvent_Dialog alertDialog = new EditEvent_Dialog();
              Bundle bundle = new Bundle();
              bundle.putString("event_name", eventName);
              alertDialog.setArguments(bundle);

              alertDialog.show(fm, "fragment_alert");
//
          }
      });

    holder.event_cardView.setOnClickListener(new View.OnClickListener() {
                      @Override
                      public void onClick(View v) {
                          boolean state=holder.EventSwitch.isChecked();
                          if (state){

                              Toast.makeText(context, eventName+" is Activated", Toast.LENGTH_SHORT).show();
                          }
                          else
                              Toast.makeText(context, eventName+" is Deactivated", Toast.LENGTH_SHORT).show();
                          }
                  });
          }

    @Override
    public int getItemCount() {
        return userListResponseData.size(); // size of the list items
    }

    class UsersViewHolder extends RecyclerView.ViewHolder {
        // init the item view's
        private TextView ed_eventname;
        private ImageView ic_event_edit;
        private Switch EventSwitch;
        private CardView event_cardView;

        public UsersViewHolder(View itemView) {
            super(itemView);
            // get the reference of item view's
            ed_eventname = (TextView) itemView.findViewById(R.id.fetchevent_name);
            ic_event_edit = (ImageView) itemView.findViewById(R.id.edit_event);
            EventSwitch = (Switch) itemView.findViewById(R.id.event_switch);
            event_cardView = (CardView) itemView.findViewById(R.id.event_list_card);
        }
    }
}

::::event_list_items.xml::::

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/event_list_card"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    card_view:cardBackgroundColor="@color/colorPrimary"
    card_view:cardCornerRadius="10dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:background="@color/colorPrimary">

        <TextView
        android:id="@+id/fetchevent_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#fff"
        android:layout_gravity="left|center"
        android:padding="5dp"
        android:textSize="15sp"
        android:layout_margin="5dp"
        />
        <ImageView
            android:id="@+id/edit_event"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/pencilicon"
            android:layout_margin="5dp"
            android:layout_gravity="center"

            android:padding="5dp"/>
        <Switch
            android:id="@+id/event_switch"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_gravity="end|center"
            android:theme="@style/SwitchCompatTheme" />
    </LinearLayout>
</android.support.v7.widget.CardView>

:::: Recyclerview in main layout ::::

 <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView_event"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

::::EDIT::::

EventModel.java

public class EventModel {

    public String event_name;

    public EventModel(String event_name){
        this.event_name = event_name;
    }

    public EventModel(){

    }

    public void setEvent_name(String event_name) {
        this.event_name = event_name;
    }

    public String getEvent_name() {
        return event_name;
    }
}
Zoe
  • 27,060
  • 21
  • 118
  • 148

2 Answers2

2

The problem here is that you are checking if a switch is checked from the view and not from the model. The model needs to have a boolean variable isChecked, that determines whether the view is checked or not. You should never check if a view is checked from the view. For example,

Add this to your onBindViewHolder:

 holder.EventSwitch.setOnClickListener(new View.OnClickListener() {
                      @Override
                      public void onClick(View v) {
                          boolean state=userListResponseData.get(position).isChecked();
                          userListResponseData.get(position).setChecked(!state);

                  });
          }


      holder.EventSwitch.setChecked(userListResponseData.get(position).isChecked());

And change your model to the following:

 public class EventModel {

        public String event_name;

        private boolean checked;

        public boolean isChecked(){
            return checked;
        }
        public void setChecked(boolean checked){
            this.checked = checked;

        }
        public EventModel(String event_name){
            this.event_name = event_name;
        }

        public EventModel(){

        }

        public void setEvent_name(String event_name) {
            this.event_name = event_name;
        }

        public String getEvent_name() {
            return event_name;
        }


}
Alan Deep
  • 2,037
  • 1
  • 14
  • 22
  • First off- he isn't checking in the view, he's checking in the handler. Secondly, that may be a philosophical problem if he was, but it isn't a bug. His issue is not properly setting the check state on view recycle. – Gabe Sechan Mar 16 '19 at 07:12
  • you mean i should create a function in my model class then it should check it ? – prashant kumar singh Mar 16 '19 at 07:12
  • yes, the conditions are not creating any problems even if i removed them all it behaves same on front end. – prashant kumar singh Mar 16 '19 at 07:18
  • @GabeSechan yes he is `boolean state=holder.EventSwitch.isChecked();` – Alan Deep Mar 16 '19 at 07:21
  • hey brother thanks for the help it is working very much fine now – prashant kumar singh Mar 16 '19 at 07:41
  • hey, I also want to save this state to backend whenever toogle swtich i hope it will not make any problem there ?? can you suggest me something on that? – prashant kumar singh Mar 16 '19 at 07:42
  • @prashantkumarsingh always welcome! In the EventSwitch.setOnClickListener method you can save your all your userListResponseData to backend. I don't know if I got your question well. – Alan Deep Mar 16 '19 at 07:52
  • Okay thanks for all the help, now i will be working on saving these states so i can get them as they were set on page reloads.. may be some getchecked() or something will do the trick. – prashant kumar singh Mar 16 '19 at 07:56
  • one ques i have here is, what you did here ? you are setting the clicked switch true opp of ischecked() and the rest as ischecked() ?? – prashant kumar singh Mar 16 '19 at 07:57
  • Whenever you are clicking the switch, we get the binding model's current state and toggle its state. The switch is taking its appropriate state according to the line added in the end: holder.EventSwitch.setChecked(userListResponseData.get(position).isChecked()); – Alan Deep Mar 16 '19 at 08:00
  • If you're satisfied with my answer please mark it as best answer! Thank you! – Alan Deep Mar 16 '19 at 08:01
  • okay thanks for making it clear alan, now i can move ahead on saving and getting them from server ...!!! – prashant kumar singh Mar 16 '19 at 08:02
  • As @GabeSechan said i should save these states so that on page reloads and scrolls they can be recovered. I somehow did so. Now i switched on the first switch and when reload the page i get the on switch but when i scroll down and again come up it was off then on method call i got switched on again..... do you have any suggestion for this ? – prashant kumar singh Mar 16 '19 at 11:54
0

This is due to View recycling (from the name of the class). Views are reused and bound to new values as the list scrolls. To make this work, your bind function MUST set the check state every time its called. And your check listeners need to write the check state to a model class somewhere, so if you scroll that position off the screen and then back on your can restore it.

The entire point of recycler view is to get that recycling behavior so you can minimize memory usage of views (which are very heavyweight). Its not just to show things in a list. It seems like you didn't understand how the class works.

Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127
  • hey thanks for responding , and yes i understand recyclerview behaviours a bit but i thought it would work but it didn't. Can you please brief me more on thins and how i can make it work. – prashant kumar singh Mar 16 '19 at 07:07
  • Basically, your onBindViewHolder needs to set EVERYTHING that can be changed by either the user or the app EVERY time its called. Anything else will lead to bugs like you see in yours. This means the checked state needs to be set in each call to onBindViewHolder. ANd as I stated, your click listener needs to save that state so you can restore it when you scroll back (or you'll lose it if you scroll off screen). – Gabe Sechan Mar 16 '19 at 07:11
  • okay you mean i should save the states on evey check change so i can use them on page reloads ? – prashant kumar singh Mar 16 '19 at 07:20
  • Not just page reloads, scrolls as well. As you scroll, if there are 10 views per screen, the same the is used for index 0, 10, 20, etc. If you want the value to be right when you scroll back, you have to save them to a model somewhere – Gabe Sechan Mar 16 '19 at 07:22
  • okay, i understand this more than ever but i have a question, acc to current situation if toogle the 1st switch saves it to backend through some method calls , will it not saves the 10th switch as well? or it will work like while saving i will only call method for 1st switch like ` model.get(position).setState("true")` ???? – prashant kumar singh Mar 16 '19 at 07:29