7

I have a recyclerview which populates data from SQL database. Now each row in the recyclerview has a seekbar which when moved displays it's progress in a textview inside the same row. The problem is when I scroll the recyclerview up or down then return back to the first changed row, the seekbar is returned to its default position. How can I make it save the new position ? In normal activities/fragments I use lifecycle methods as "onPause" to save/restore the state. Here we have onAttachedToRecyclerView, I think it should solve my problem but I don't know exactly how.

EDIT : here is a full simple app files which I'm working on to test this problem.

MainActivity.class

public class MainActivity extends AppCompatActivity {
    private List<Score> scoreList = new ArrayList<>();
    private RecyclerView recyclerView;
    private MyAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);

    mAdapter = new MyAdapter(scoreList);
    RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
    recyclerView.setLayoutManager(mLayoutManager);
    recyclerView.setItemAnimator(new DefaultItemAnimator());
    recyclerView.setAdapter(mAdapter);

    prepareScoreData();
}

private void prepareScoreData() {
    Score score = new Score("title", 5);
    scoreList.add(score);

    for(int i= 0; i<1000; i++){
        score = new Score("title", 5);
        scoreList.add(score);
    }

    mAdapter.notifyDataSetChanged();
}
}

MyAdapter

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private List<Score> scoresList;

public class MyViewHolder extends RecyclerView.ViewHolder {
    TextView title, scoreView;
    SeekBar seekbar;

    public MyViewHolder(View view) {
        super(view);
        title = (TextView) view.findViewById(R.id.title);
        scoreView = (TextView) view.findViewById(R.id.score);
        seekbar = (SeekBar) view.findViewById(R.id.seekbar);
    }
}


public MyAdapter(List<Score> scoresList) {
    this.scoresList = scoresList;
}

@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View itemView = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.item, parent, false);

    return new MyViewHolder(itemView);
}


@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
    final Score score = scoresList.get(position);
    holder.title.setText(score.getTitle());

    if (!score.getProgressed()) {
        holder.seekbar.setProgress(0) ;
    } else {
        holder.seekbar.setProgress(score.getSeekbarProgress());
    }

    holder.seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

        @Override
        public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
            holder.scoreView.setText(String.valueOf(i));
            score.setSeekbarProgress(i);
            score.setProgressed(true);
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
        }
    });
}

@Override
public int getItemCount() {
    return scoresList.size();
}
}

Score class

public class Score {
private String title;
int seekbarProgress;
boolean progressed;

public Score() {
}

public Score(String title,int seekbarProgress) {
    this.title = title;
    this.seekbarProgress = seekbarProgress;
}

public void setProgressed(boolean progressed) {
    this.progressed = progressed;
}



public void setTitle(String title) {
    this.title = title;
}

public void setSeekbarProgress(int seekbarProgress) {
    this.seekbarProgress = seekbarProgress;
}

public String getTitle() {
    return title;
}

public int getSeekbarProgress() {
    return seekbarProgress;
}

public boolean getProgressed() {
    return progressed;
}
}

MainActivity_Layout

    <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.moaness.tut_recyclerview.MainActivity">

    <!-- A RecyclerView with some commonly used attributes -->
    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    </RelativeLayout>

item.xml

    <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:focusable="true"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="60dp"
    android:paddingBottom="60dp"
    android:layout_marginBottom="10dp"
    android:clickable="true"
    android:background="#f2f2f2"
    android:orientation="vertical">

    <TextView
        android:id="@+id/title"
        android:text="title"
        android:textColor="@color/title"
        android:textSize="16dp"
        android:paddingTop="16dp"
        android:textStyle="bold"
        android:layout_alignParentTop="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/score"
        android:text="score"
        android:layout_below="@+id/title"
        android:textSize="16dp"
        android:paddingBottom="16dp"
        android:textStyle="bold"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <SeekBar
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/score"
        android:id="@+id/seekbar"
        />

</RelativeLayout>
Bialy
  • 905
  • 2
  • 12
  • 22
  • Everytime the view of the row goes out then back into view, it'll re-populate it from whatever method/data you're providing it. You'll need to SAVE your seekbar data into a db like your SQL, locally, then include that logic in how you populate your rows. – TWL Aug 13 '16 at 05:17
  • I can't believe this hard coding style is the only method here :( there must be a simpler approach for small data changes like this – Bialy Aug 13 '16 at 05:21
  • Provide code-snippets for us to better understand your query! – Smit Aug 13 '16 at 05:39

1 Answers1

13

If you are using recyclerview you need to maintain states of each row, means if you are checking using a condition(i.e. if) at any stage of recyclerview item(in recyclerview adapter class) then you need to handle else as well. I can send you a code snippet so you can have a good idea for recyclerview adapter.

public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.ViewHolder> {
    List<ViewHolder> holders = new ArrayList<ViewHolder>();
    private ArrayList<ContactModel> arrayList = new ArrayList<>();
    private Context context;
    private LayoutInflater inflater;

    public void clearAdapter() {
        arrayList.clear();
        notifyDataSetChanged();
    }

    public ContactsAdapter(Context context, ArrayList<ContactModel> arrayList) {
        this.context = context;
        this.arrayList = arrayList;
    }

    public void setList(ArrayList<ContactModel> listSearch) {
        this.arrayList = listSearch;
        notifyItemRangeChanged(0, listSearch.size());
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.custom_row_for_contacts, parent, false);
        ViewHolder viewHolder = new ViewHolder(view);
        holders.add(viewHolder);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
        final ContactModel current = this.arrayList.get(position);
        holder.txtDriverName.setText(current.getName());
        holder.txtDriverPhone.setText(current.getPhone());
        if (current.getImgUrl().length() > 0) {
            String urlLicenceThumb = UrlEndPoints.parentUrl + current.getImgUrl();
            Glide.with(context).load(urlLicenceThumb).error(R.mipmap.ic_launcher).into(holder.imgDriver);
        } else {
           Glide.with(context).load(R.mipmap.ic_launcher).into(holder.imgDriver);
        }
    }

    public void delete(int position) {
        arrayList.remove(position);
        notifyItemRemoved(position);
    }

    @Override
    public int getItemCount() {
        return (null != arrayList ? arrayList.size() : 0);
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        private TextView txtDriverName, txtDriverPhone;
        private CircleImageView imgDriver;
        private Button btnInvite;
        private CheckBox chkAdd;

        public ViewHolder(View itemView) {
            super(itemView);
            chkAdd = (CheckBox) itemView.findViewById(R.id.chkAdd);
            imgDriver = (CircleImageView) itemView.findViewById(R.id.imgDriver);
            txtDriverName = (TextView)itemView.findViewById(R.id.txtDriverName);
            txtDriverPhone = (TextView)     itemView.findViewById(R.id.txtDriverPhone);
            btnInvite = (Button) itemView.findViewById(R.id.btnInvite);
        }
    }
}
Maddy
  • 4,525
  • 4
  • 33
  • 53
  • yes please, a code snippet would be more than good. many thanks – Bialy Aug 13 '16 at 05:22
  • Thanks, but really I don't understand how would this if/else statement save the state? let's suppose you have a checkbox as you wrote, how can I save its checked/unchecked state after list scroll ? – Bialy Aug 13 '16 at 07:05
  • if (current.isChecked()) {//---some condition holder.chkAdd.setText("Checked"); } else { holder.chkAdd.setText("Un Checked"); } – Maddy Aug 13 '16 at 07:19
  • 1
    Great, you cleared a BIG misunderstanding for me using the recyclerview as they were "caching?" values from top rows to the below rows with no predefined values. Now the problem is still the same, when I change the scrollbar value , scrolldown then return up , it returns back to 0 :( – Bialy Aug 13 '16 at 07:59
  • I had the same issue as yours, I had a list of contacts which i need to put in recyclerview, each contact has a check box, when i check the boxes after scroll it was misbehaving, I already solved this issue for my recyclerview with my this adapter.I have shown you the way where you need to save the state in your model class object(in this case i.e. current), you have to save the check box values in that object. – Maddy Aug 13 '16 at 08:52
  • After some struggling with the code using the same principle you provided, I can say it finally works :) MUCH THANKS ! – Bialy Aug 13 '16 at 09:52
  • 1
    @Baily can you provide some light into how you solved the problem? I am also having the same problem http://stackoverflow.com/questions/40142216/save-state-of-the-item-selected-in-recyclerview-when-multiple-items-selected/40142339?noredirect=1#comment67706536_40142339 – Gaurav Sarma Oct 24 '16 at 15:10
  • what if we have multiple if , else if conditions inside onBindViewHolder()? what should be there inside else { } part? is it default value to be set here! – Radhey Mar 20 '18 at 05:51
  • @Radhey Yes exactly! – Maddy Mar 20 '18 at 05:56